diff --git a/DEPS b/DEPS
index 98c00b4e..57ea51f 100644
--- a/DEPS
+++ b/DEPS
@@ -245,7 +245,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '40df2e3cf51e401a7c052b4a014cf4d315e75e5a',
+  'skia_revision': 'b292c30aa0c63fe858d8b68393629ebd4ca734c4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -276,7 +276,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '25ad42aabeaba6227f37795cdd2752e128e83827',
+  'googletest_revision': '14aa11db02d9851d957f93ef9fddb110c1aafdc6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '87e60660bd20d8397e7f699432def54a1a3e4e51',
+  'catapult_revision': 'b4974c8f859b9ca0e5c7fa03588cb9011563d522',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -360,7 +360,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '7b76192467210e2d3f57782bad5c867ce96f9c7a',
+  'dawn_revision': '9fb7a5146ab6102dbcd73bb7e9053f5f14e4dd98',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -663,7 +663,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'ea30b13ece31584e71b30f8c60b37e1a8109f2a7',
+    'url': Var('chromium_git') + '/website.git' + '@' + '80a87e883449376fa2b7ba82ebc81b177949d993',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1041,7 +1041,7 @@
   },
 
   'src/third_party/cast_core/public/src':
-    Var('chromium_git') + '/cast_core/public' + '@' + '1c3981386ac760d99f38e25f8cc0ee6c68c47f4d',
+    Var('chromium_git') + '/cast_core/public' + '@' + 'f5c497a379f45375b0bc0148934c461e50f64ba4',
 
   'src/third_party/catapult':
     Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
@@ -1090,13 +1090,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'df01c5ad5be408cd157b879a1cce983f90762765',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '91d2a5db01e886afa0ffdbe1b73ad6ac6aa3ee95',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/dom_distiller_js/dist':
-    Var('chromium_git') + '/chromium/dom-distiller/dist.git' + '@' + 'f339eb9463714c3d31657c8ee1bd53d1c7e5c555',
+    Var('chromium_git') + '/chromium/dom-distiller/dist.git' + '@' + '199de96b345ada7c6e7e6ba3d2fa7a6911b8767d',
 
   'src/third_party/eigen3/src':
     Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '7db0ac977acf276fb0817cfb89e490cdbae0ab56',
@@ -1694,7 +1694,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '729be85c2f01876a1ea075c523d542ebaaa76301',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'bab128555afa0f94994a5d5689b7d8da930cdee1',
+    Var('webrtc_git') + '/src.git' + '@' + 'ff246c1c0479d8a5af172c179aa01c327daeb222',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1764,7 +1764,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dbb4c1d3b6dfbeefa22e3d76ba4d4e95df7fff9d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@27a3a9e84c95c36df6803db7b70b0d9961bb0559',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
index ffb2131..1785c7f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
@@ -38,7 +38,7 @@
  */
 @RunWith(AwJUnit4ClassRunner.class)
 @OnlyRunIn(SINGLE_PROCESS) // These are unit tests. No need to repeat for multiprocess.
-@Batch(Batch.UNIT_TESTS)
+@Batch(Batch.PER_CLASS)
 public class AwContentsClientCallbackHelperTest {
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index cff85623..1718b27 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1083,18 +1083,8 @@
     "system/holding_space/holding_space_item_view.h",
     "system/holding_space/holding_space_item_views_section.cc",
     "system/holding_space/holding_space_item_views_section.h",
-    "system/holding_space/holding_space_progress_icon_animation.cc",
-    "system/holding_space/holding_space_progress_icon_animation.h",
     "system/holding_space/holding_space_progress_indicator.cc",
     "system/holding_space/holding_space_progress_indicator.h",
-    "system/holding_space/holding_space_progress_indicator_animation.cc",
-    "system/holding_space/holding_space_progress_indicator_animation.h",
-    "system/holding_space/holding_space_progress_ring_animation.cc",
-    "system/holding_space/holding_space_progress_ring_animation.h",
-    "system/holding_space/holding_space_progress_ring_indeterminate_animation.cc",
-    "system/holding_space/holding_space_progress_ring_indeterminate_animation.h",
-    "system/holding_space/holding_space_progress_ring_pulse_animation.cc",
-    "system/holding_space/holding_space_progress_ring_pulse_animation.h",
     "system/holding_space/holding_space_tray.cc",
     "system/holding_space/holding_space_tray.h",
     "system/holding_space/holding_space_tray_bubble.cc",
@@ -1432,8 +1422,18 @@
     "system/privacy_screen/privacy_screen_toast_controller.h",
     "system/privacy_screen/privacy_screen_toast_view.cc",
     "system/privacy_screen/privacy_screen_toast_view.h",
+    "system/progress_indicator/progress_icon_animation.cc",
+    "system/progress_indicator/progress_icon_animation.h",
+    "system/progress_indicator/progress_indicator_animation.cc",
+    "system/progress_indicator/progress_indicator_animation.h",
     "system/progress_indicator/progress_indicator_animation_registry.cc",
     "system/progress_indicator/progress_indicator_animation_registry.h",
+    "system/progress_indicator/progress_ring_animation.cc",
+    "system/progress_indicator/progress_ring_animation.h",
+    "system/progress_indicator/progress_ring_indeterminate_animation.cc",
+    "system/progress_indicator/progress_ring_indeterminate_animation.h",
+    "system/progress_indicator/progress_ring_pulse_animation.cc",
+    "system/progress_indicator/progress_ring_pulse_animation.h",
     "system/rotation/rotation_lock_feature_pod_controller.cc",
     "system/rotation/rotation_lock_feature_pod_controller.h",
     "system/scheduled_feature/scheduled_feature.cc",
diff --git a/ash/app_list/model/app_list_item_list_unittest.cc b/ash/app_list/model/app_list_item_list_unittest.cc
index f62cc539..a2d79a0 100644
--- a/ash/app_list/model/app_list_item_list_unittest.cc
+++ b/ash/app_list/model/app_list_item_list_unittest.cc
@@ -43,10 +43,15 @@
     item_list_->SetItemPosition(item_list_->FindItem(id), new_position);
   }
   void RequestMoveItemToFolder(std::string id,
-                               const std::string& folder_id,
-                               RequestMoveToFolderReason reason) override {}
+                               const std::string& folder_id) override {}
   void RequestMoveItemToRoot(std::string id,
                              syncer::StringOrdinal target_position) override {}
+  std::string RequestFolderCreation(std::string merge_target_id,
+                                    std::string item_to_merge_id) override {
+    return "";
+  }
+  void RequestFolderRename(std::string folder_id,
+                           const std::string& name) override {}
   void RequestAppListSort(AppListSortOrder order) override {}
   void RequestAppListSortRevert() override {}
 
diff --git a/ash/app_list/model/app_list_model.cc b/ash/app_list/model/app_list_model.cc
index b2e0aaa..23241c9 100644
--- a/ash/app_list/model/app_list_model.cc
+++ b/ash/app_list/model/app_list_model.cc
@@ -156,30 +156,11 @@
       LOG(WARNING) << "MergeItems called with OEM folder as target";
       return "";
     }
-    delegate_->RequestMoveItemToFolder(source_item_id, target_item_id,
-                                       RequestMoveToFolderReason::kMoveItem);
+    delegate_->RequestMoveItemToFolder(source_item_id, target_item_id);
     return target_folder->id();
   }
 
-  // Create a new folder in the same location as the target item.
-  std::string new_folder_id = AppListFolderItem::GenerateId();
-  DVLOG(2) << "Creating folder for merge: " << new_folder_id;
-  std::unique_ptr<AppListItem> new_folder_ptr =
-      std::make_unique<AppListFolderItem>(new_folder_id, delegate_);
-  new_folder_ptr->set_position(target_item->position());
-  AppListFolderItem* new_folder =
-      static_cast<AppListFolderItem*>(AddItemToRootListAndNotify(
-          std::move(new_folder_ptr), ReparentItemReason::kAdd));
-
-  // Add the items to the new folder.
-  delegate_->RequestMoveItemToFolder(
-      target_item_id, new_folder_id,
-      RequestMoveToFolderReason::kMergeFirstItem);
-  delegate_->RequestMoveItemToFolder(
-      source_item_id, new_folder_id,
-      RequestMoveToFolderReason::kMergeSecondItem);
-
-  return new_folder->id();
+  return delegate_->RequestFolderCreation(target_item_id, source_item_id);
 }
 
 void AppListModel::MoveItemToFolder(AppListItem* item,
diff --git a/ash/app_list/model/app_list_test_model.cc b/ash/app_list/model/app_list_test_model.cc
index f026dbb..452c948 100644
--- a/ash/app_list/model/app_list_test_model.cc
+++ b/ash/app_list/model/app_list_test_model.cc
@@ -86,12 +86,9 @@
   SetItemMetadata(id, std::move(metadata));
 }
 
-void AppListTestModel::RequestMoveItemToFolder(
-    std::string id,
-    const std::string& folder_id,
-    RequestMoveToFolderReason reason) {
+void AppListTestModel::RequestMoveItemToFolder(std::string id,
+                                               const std::string& folder_id) {
   // Copy the logic of `ChromeAppListModelUpdater::HandleMoveItemToFolder()`.
-
   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
   const syncer::StringOrdinal target_position =
       dest_folder->item_list()->CreatePositionBefore(syncer::StringOrdinal());
@@ -112,6 +109,39 @@
   SetItemMetadata(id, std::move(metadata));
 }
 
+std::string AppListTestModel::RequestFolderCreation(
+    std::string merge_target_id,
+    std::string item_to_merge_id) {
+  auto target_item_metadata = FindItem(merge_target_id)->CloneMetadata();
+  const syncer::StringOrdinal target_item_position =
+      target_item_metadata->position;
+
+  const std::string folder_id = AppListFolderItem::GenerateId();
+  auto folder = std::make_unique<AppListFolderItem>(
+      folder_id, /*app_list_model_delegate=*/this);
+  auto folder_metadata = folder->CloneMetadata();
+  folder_metadata->position = target_item_position;
+  folder->SetMetadata(std::move(folder_metadata));
+  AddItem(folder.release());
+
+  target_item_metadata->folder_id = folder_id;
+  SetItemMetadata(merge_target_id, std::move(target_item_metadata));
+
+  auto item_to_merge_metadata = FindItem(item_to_merge_id)->CloneMetadata();
+  item_to_merge_metadata->position = target_item_position.CreateAfter();
+  item_to_merge_metadata->folder_id = folder_id;
+  SetItemMetadata(item_to_merge_id, std::move(item_to_merge_metadata));
+
+  return folder_id;
+}
+
+void AppListTestModel::RequestFolderRename(std::string id,
+                                           const std::string& new_name) {
+  auto metadata = FindItem(id)->CloneMetadata();
+  metadata->name = new_name;
+  SetItemMetadata(id, std::move(metadata));
+}
+
 void AppListTestModel::RequestAppListSort(AppListSortOrder order) {
   requested_sort_order_ = order;
 }
diff --git a/ash/app_list/model/app_list_test_model.h b/ash/app_list/model/app_list_test_model.h
index 4f65273..80ff437 100644
--- a/ash/app_list/model/app_list_test_model.h
+++ b/ash/app_list/model/app_list_test_model.h
@@ -58,10 +58,13 @@
                              const syncer::StringOrdinal& new_position,
                              RequestPositionUpdateReason reason) override;
   void RequestMoveItemToFolder(std::string id,
-                               const std::string& folder_id,
-                               RequestMoveToFolderReason reason) override;
+                               const std::string& folder_id) override;
   void RequestMoveItemToRoot(std::string id,
                              syncer::StringOrdinal target_position) override;
+  std::string RequestFolderCreation(std::string merge_target_id,
+                                    std::string item_to_merge_id) override;
+  void RequestFolderRename(std::string id,
+                           const std::string& new_name) override;
   void RequestAppListSort(AppListSortOrder order) override;
   void RequestAppListSortRevert() override;
 
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index 0fdbe24c..9163d44 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -30,6 +30,7 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/view_shadow.h"
 #include "ash/search_box/search_box_constants.h"
+#include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/highlight_border.h"
 #include "base/bind.h"
@@ -44,6 +45,8 @@
 #include "ui/compositor/animation_throughput_reporter.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_type.h"
+#include "ui/events/event.h"
+#include "ui/events/event_handler.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/insets.h"
@@ -115,6 +118,45 @@
 
 }  // namespace
 
+// Makes focus traversal skip the assistant button when pressing the down arrow
+// key or the up arrow key. Normally views would move focus from the search box
+// to the assistant button on arrow down. However, the assistant button is
+// visually to the right, so this feels weird. Likewise, on arrow up from
+// continue tasks it feels better to put focus directly in the search box.
+class AssistantButtonFocusSkipper : public ui::EventHandler {
+ public:
+  explicit AssistantButtonFocusSkipper(views::View* button) : button_(button) {
+    DCHECK(button_);
+    Shell::Get()->AddPreTargetHandler(this);
+  }
+
+  ~AssistantButtonFocusSkipper() override {
+    Shell::Get()->RemovePreTargetHandler(this);
+  }
+
+  // ui::EventHandler:
+  void OnEvent(ui::Event* event) override {
+    // Don't adjust focus behavior if the user already focused the button.
+    if (button_->HasFocus())
+      return;
+
+    bool skip_focus = false;
+    // This class overrides OnEvent() to examine all events so that focus
+    // behavior is restored by mouse events, gesture events, etc.
+    if (event->type() == ui::ET_KEY_PRESSED) {
+      ui::KeyboardCode key = event->AsKeyEvent()->key_code();
+      if (key == ui::VKEY_UP || key == ui::VKEY_DOWN) {
+        skip_focus = true;
+      }
+    }
+    button_->SetFocusBehavior(skip_focus ? views::View::FocusBehavior::NEVER
+                                         : views::View::FocusBehavior::ALWAYS);
+  }
+
+ private:
+  views::View* const button_;
+};
+
 AppListBubbleView::AppListBubbleView(
     AppListViewDelegate* view_delegate,
     ApplicationDragAndDropHost* drag_and_drop_host)
@@ -192,6 +234,10 @@
   params.animate_changing_search_icon = false;
   search_box_view_->Init(params);
 
+  assistant_button_focus_skipper_ =
+      std::make_unique<AssistantButtonFocusSkipper>(
+          search_box_view_->assistant_button());
+
   // The main view has a solid color layer, so the separator needs its own
   // layer to visibly paint.
   separator_ = contents->AddChildView(std::make_unique<SeparatorWithLayer>());
@@ -508,7 +554,8 @@
 
 void AppListBubbleView::OnSearchBoxKeyEvent(ui::KeyEvent* event) {
   // Nothing to do. Search box starts focused, and FocusManager handles arrow
-  // key traversal from there.
+  // key traversal from there. AssistantButtonFocusSkipper above handles
+  // skipping the assistant button on arrow up and arrow down.
 }
 
 bool AppListBubbleView::CanSelectSearchResults() {
diff --git a/ash/app_list/views/app_list_bubble_view.h b/ash/app_list/views/app_list_bubble_view.h
index 1283e09..903d744 100644
--- a/ash/app_list/views/app_list_bubble_view.h
+++ b/ash/app_list/views/app_list_bubble_view.h
@@ -25,6 +25,7 @@
 class AppListFolderItem;
 class AppListFolderView;
 class AppListViewDelegate;
+class AssistantButtonFocusSkipper;
 class FolderBackgroundView;
 class SearchBoxView;
 class SearchResultPageDialogController;
@@ -172,6 +173,9 @@
   // Called after the hide animation ends or aborts.
   base::OnceClosure on_hide_animation_ended_;
 
+  // See class comment in .cc file.
+  std::unique_ptr<AssistantButtonFocusSkipper> assistant_button_focus_skipper_;
+
   base::WeakPtrFactory<AppListBubbleView> weak_factory_{this};
 };
 
diff --git a/ash/app_list/views/app_list_bubble_view_unittest.cc b/ash/app_list/views/app_list_bubble_view_unittest.cc
index ddaf5de..dc0e4b2 100644
--- a/ash/app_list/views/app_list_bubble_view_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_view_unittest.cc
@@ -45,6 +45,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/test_utils.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
@@ -773,6 +774,41 @@
   EXPECT_FALSE(app_item->HasFocus());
 }
 
+// Exercises AssistantButtonFocusSkipper.
+TEST_F(AppListBubbleViewTest, DownAndUpArrowSkipsAssistantButton) {
+  SimulateAssistantEnabled();
+  // Add an app, but no "Continue" suggestions.
+  AddAppItems(1);
+  ShowAppList();
+
+  auto* apps_grid_view = GetAppListTestHelper()->GetScrollableAppsGridView();
+  AppListItemView* app_item = apps_grid_view->GetItemViewAt(0);
+  SearchBoxView* search_box_view = GetSearchBoxView();
+  EXPECT_TRUE(search_box_view->search_box()->HasFocus());
+
+  // Pressing down arrow moves focus into apps.
+  PressAndReleaseKey(ui::VKEY_DOWN);
+  EXPECT_FALSE(search_box_view->search_box()->HasFocus());
+  EXPECT_FALSE(search_box_view->assistant_button()->HasFocus());
+  EXPECT_TRUE(app_item->HasFocus());
+
+  // Pressing up arrow moves focus back to search box.
+  PressAndReleaseKey(ui::VKEY_UP);
+  EXPECT_TRUE(search_box_view->search_box()->HasFocus());
+  EXPECT_FALSE(search_box_view->assistant_button()->HasFocus());
+  EXPECT_FALSE(app_item->HasFocus());
+
+  // Tab key moves focus to assistant button.
+  PressAndReleaseKey(ui::VKEY_TAB);
+  EXPECT_FALSE(search_box_view->search_box()->HasFocus());
+  EXPECT_TRUE(search_box_view->assistant_button()->HasFocus());
+
+  // Shift-tab moves focus back to search box.
+  PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
+  EXPECT_TRUE(search_box_view->search_box()->HasFocus());
+  EXPECT_FALSE(search_box_view->assistant_button()->HasFocus());
+}
+
 TEST_F(AppListBubbleViewTest, DownArrowSelectsRecentsThenApps) {
   // Create enough apps to require scrolling.
   AddAppItems(50);
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index 7a207126..31cfa13 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -30,6 +30,7 @@
 #include "ash/public/cpp/app_list/app_list_color_provider.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/app_list/app_list_model_delegate.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/public/cpp/style/color_provider.h"
@@ -1206,7 +1207,8 @@
 
 void AppListFolderView::SetItemName(AppListFolderItem* item,
                                     const std::string& name) {
-  AppListModelProvider::Get()->model()->SetItemName(item, name);
+  AppListModelProvider::Get()->model()->delegate()->RequestFolderRename(
+      folder_item_->id(), name);
 }
 
 const AppListConfig* AppListFolderView::GetAppListConfig() const {
diff --git a/ash/app_list/views/app_list_toast_view.cc b/ash/app_list/views/app_list_toast_view.cc
index 86e8d9c..cac2a79 100644
--- a/ash/app_list/views/app_list_toast_view.cc
+++ b/ash/app_list/views/app_list_toast_view.cc
@@ -224,7 +224,8 @@
       !features::IsDarkLightModeEnabled() ||
               AshColorProvider::Get()->IsDarkModeEnabled()
           ? *dark_icon_
-          : *light_icon_));
+          : *light_icon_,
+      ui::kColorAshSystemUIMenuIcon));
 }
 
 void AppListToastView::CreateIconView() {
diff --git a/ash/app_list/views/apps_grid_context_menu.cc b/ash/app_list/views/apps_grid_context_menu.cc
index 1d26f2b3..c1ee621 100644
--- a/ash/app_list/views/apps_grid_context_menu.cc
+++ b/ash/app_list/views/apps_grid_context_menu.cc
@@ -74,12 +74,14 @@
       AppsGridCommandId::kReorderByNameAlphabetical,
       l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_APPS_GRID_CONTEXT_MENU_REORDER_BY_NAME),
-      ui::ImageModel::FromVectorIcon(kSortAlphabeticalIcon));
+      ui::ImageModel::FromVectorIcon(kSortAlphabeticalIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
   context_menu_model_->AddItemWithIcon(
       AppsGridCommandId::kReorderByColor,
       l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_APPS_GRID_CONTEXT_MENU_REORDER_BY_COLOR),
-      ui::ImageModel::FromVectorIcon(kSortColorIcon));
+      ui::ImageModel::FromVectorIcon(kSortColorIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
 }
 
 void AppsGridContextMenu::OnMenuClosed() {
diff --git a/ash/app_list/views/continue_task_view.cc b/ash/app_list/views/continue_task_view.cc
index 88fb32e3..970341a 100644
--- a/ash/app_list/views/continue_task_view.cc
+++ b/ash/app_list/views/continue_task_view.cc
@@ -252,13 +252,15 @@
       ContinueTaskCommandId::kOpenResult,
       l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_OPEN),
-      ui::ImageModel::FromVectorIcon(kLaunchIcon));
+      ui::ImageModel::FromVectorIcon(kLaunchIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
 
   context_menu_model_->AddItemWithIcon(
       ContinueTaskCommandId::kRemoveResult,
       l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_REMOVE),
-      ui::ImageModel::FromVectorIcon(kRemoveOutlineIcon));
+      ui::ImageModel::FromVectorIcon(kRemoveOutlineIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
 
   return context_menu_model_.get();
 }
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 5eb33e0..b237cad 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3402,6 +3402,10 @@
       <message name="IDS_ASH_CALENDAR_BUBBLE_ACCESSIBLE_DESCRIPTION" desc="The accessible description of the calendar bubble and the information in it.">
         Calendar, <ph name="current_month_year">$1<ex>August 2021</ex></ph>
       </message>
+      
+      <message name="IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION" desc="The accessible description of the calendar entry button.">
+       <ph name="current_time">$1<ex>Aug 2021 11:25AM</ex></ph>, Press enter key to open Calendar view
+      </message>
 
       <message name="IDS_ASH_CALENDAR_TITLE" desc="The label used as the title of the unified system tray calendar view.">
         Calendar
@@ -3455,6 +3459,10 @@
         <ph name="date">$1<ex>August 31, 2021</ex></ph>, <ph name="number">$2<ex>8</ex></ph> events
       </message>
 
+      <message name="IDS_ASH_CALENDAR_DATE_CELL_ON_FOCUS_ACCESSIBLE_DESCRIPTION" desc="The accessible label of a calendar date cell, which is on focused for the first time.">
+        <ph name="date_cell_tool_tip">$1<ex>August 31, 2021, 2 events</ex></ph>. Use arrow keys to navigate between dates.
+      </message>
+
       <message name="IDS_ASH_CALENDAR_SELECTED_DATE_CELL_ACCESSIBLE_DESCRIPTION" desc="The accessible label of the selected calendar date cell.">
         Calendar, week of <ph name="date">$1<ex>July 16th 2021,</ex></ph>, <ph name="selected_date">$2<ex>18</ex></ph> is currently selected.
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_CALENDAR_DATE_CELL_ON_FOCUS_ACCESSIBLE_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_CALENDAR_DATE_CELL_ON_FOCUS_ACCESSIBLE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..f9a5bd7
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_CALENDAR_DATE_CELL_ON_FOCUS_ACCESSIBLE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+f2567b2840b476d69da6bfa56c75d0712f9d19d0
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..0270d28
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+a3d202370b6f857389ffe3e0b0bb8fe811c58abc
\ No newline at end of file
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 881dfe65..fae60fd 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -429,8 +429,7 @@
 
 void ForgetNetworkFailureCallback(
     base::OnceCallback<void(arc::mojom::NetworkResult)> callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   std::move(callback).Run(arc::mojom::NetworkResult::FAILURE);
 }
 
@@ -441,8 +440,7 @@
 
 void StartConnectFailureCallback(
     base::OnceCallback<void(arc::mojom::NetworkResult)> callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   std::move(callback).Run(arc::mojom::NetworkResult::FAILURE);
 }
 
@@ -453,16 +451,14 @@
 
 void StartDisconnectFailureCallback(
     base::OnceCallback<void(arc::mojom::NetworkResult)> callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   std::move(callback).Run(arc::mojom::NetworkResult::FAILURE);
 }
 
 void ArcVpnSuccessCallback() {}
 
 void ArcVpnErrorCallback(const std::string& operation,
-                         const std::string& error_name,
-                         std::unique_ptr<base::DictionaryValue> error_data) {
+                         const std::string& error_name) {
   NET_LOG(ERROR) << "ArcVpnErrorCallback: " << operation << ": " << error_name;
 }
 
@@ -619,8 +615,7 @@
 
 void ArcNetHostImpl::CreateNetworkFailureCallback(
     base::OnceCallback<void(const std::string&)> callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   NET_LOG(ERROR) << "CreateNetworkFailureCallback: " << error_name;
   std::move(callback).Run(std::string());
 }
diff --git a/ash/components/arc/net/arc_net_host_impl.h b/ash/components/arc/net/arc_net_host_impl.h
index 4fafaa7f..a608171 100644
--- a/ash/components/arc/net/arc_net_host_impl.h
+++ b/ash/components/arc/net/arc_net_host_impl.h
@@ -175,8 +175,7 @@
 
   void CreateNetworkFailureCallback(
       base::OnceCallback<void(const std::string&)> callback,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+      const std::string& error_name);
 
   // Callback for chromeos::NetworkHandler::GetShillProperties
   void ReceiveShillProperties(const std::string& service_path,
diff --git a/ash/components/fwupd/BUILD.gn b/ash/components/fwupd/BUILD.gn
index 601e019..bfbc207 100644
--- a/ash/components/fwupd/BUILD.gn
+++ b/ash/components/fwupd/BUILD.gn
@@ -32,6 +32,7 @@
   deps = [
     ":fwupd",
     "//ash",
+    "//ash:test_support",
     "//ash/constants",
     "//ash/public/cpp",
     "//ash/public/mojom",
diff --git a/ash/components/fwupd/firmware_update_manager.cc b/ash/components/fwupd/firmware_update_manager.cc
index b4752e0..ab054c2 100644
--- a/ash/components/fwupd/firmware_update_manager.cc
+++ b/ash/components/fwupd/firmware_update_manager.cc
@@ -310,6 +310,12 @@
 
 // Query all updates for all devices.
 void FirmwareUpdateManager::RequestAllUpdates() {
+  if (should_show_notification_for_test_) {
+    // Short circuit to immediately display notification.
+    NotifyCriticalFirmwareUpdateReceived();
+    return;
+  }
+
   if (is_fetching_updates_) {
     return;
   }
@@ -649,4 +655,5 @@
   update_progress_observer_.reset();
   update_progress_observer_.Bind(std::move(observer));
 }
+
 }  // namespace ash
diff --git a/ash/components/fwupd/firmware_update_manager.h b/ash/components/fwupd/firmware_update_manager.h
index 92061f4..223a3a8 100644
--- a/ash/components/fwupd/firmware_update_manager.h
+++ b/ash/components/fwupd/firmware_update_manager.h
@@ -108,6 +108,10 @@
       mojo::PendingReceiver<firmware_update::mojom::UpdateProvider>
           pending_receiver);
 
+  void set_should_show_notification_for_test(bool show_notification) {
+    should_show_notification_for_test_ = show_notification;
+  }
+
  protected:
   friend class FirmwareUpdateManagerTest;
   // Temporary auxiliary variables for testing.
@@ -209,6 +213,9 @@
   // Whether or not fetching updates in inflight.
   bool is_fetching_updates_ = false;
 
+  // Used only for testing to force notification to appear.
+  bool should_show_notification_for_test_ = false;
+
   // Remotes for tracking observers that will be notified of changes to the
   // list of firmware updates.
   mojo::RemoteSet<firmware_update::mojom::UpdateObserver>
diff --git a/ash/components/fwupd/firmware_update_manager_unittest.cc b/ash/components/fwupd/firmware_update_manager_unittest.cc
index e6ce542..774d8db 100644
--- a/ash/components/fwupd/firmware_update_manager_unittest.cc
+++ b/ash/components/fwupd/firmware_update_manager_unittest.cc
@@ -12,7 +12,9 @@
 #include "ash/components/fwupd/fake_fwupd_download_client.h"
 #include "ash/components/fwupd/histogram_util.h"
 #include "ash/constants/ash_features.h"
+#include "ash/shell.h"
 #include "ash/system/firmware_update/firmware_update_notification_controller.h"
+#include "ash/test/ash_test_base.h"
 #include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom-test-utils.h"
 #include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
 #include "base/files/file.h"
@@ -24,6 +26,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chromeos/dbus/fwupd/fwupd_client.h"
+#include "components/user_manager/user_type.h"
 #include "dbus/message.h"
 #include "dbus/mock_bus.h"
 #include "dbus/mock_object_proxy.h"
@@ -34,6 +37,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/message_center/fake_message_center.h"
+#include "ui/message_center/message_center.h"
 
 using chromeos::FwupdClient;
 using message_center::MessageCenter;
@@ -66,6 +70,8 @@
 const char kDownloadDir[] = "firmware-updates";
 const char kCacheDir[] = "cache";
 const char kCabExtension[] = ".cab";
+const char kFirmwareUpdateNotificationId[] =
+    "cros_firmware_update_notification_id";
 
 void RunResponseCallback(dbus::ObjectProxy::ResponseOrErrorCallback callback,
                          std::unique_ptr<dbus::Response> response) {
@@ -123,31 +129,11 @@
       this};
 };
 
-class MockMessageCenter : public message_center::FakeMessageCenter {
- public:
-  MockMessageCenter() = default;
-
-  MockMessageCenter(const MockMessageCenter&) = delete;
-  MockMessageCenter& operator=(const MockMessageCenter&) = delete;
-
-  ~MockMessageCenter() override = default;
-
-  int notification_count() const { return notification_count_; }
-
-  // message_center::FakeMessageCenter overrides:
-  void AddNotification(std::unique_ptr<Notification> notification) override {
-    notification_count_++;
-  }
-
- private:
-  int notification_count_ = 0;
-};
-
 }  // namespace
 
 namespace ash {
 
-class FirmwareUpdateManagerTest : public testing::Test {
+class FirmwareUpdateManagerTest : public AshTestBase {
  public:
   FirmwareUpdateManagerTest() {
     scoped_feature_list_.InitAndEnableFeature(
@@ -174,13 +160,8 @@
     firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
     firmware_update_manager_->BindInterface(
         update_provider_remote_.BindNewPipeAndPassReceiver());
-    message_center_ = std::make_unique<MockMessageCenter>();
-    firmware_update_notification_controller_ =
-        std::make_unique<FirmwareUpdateNotificationController>(
-            message_center_.get());
-    firmware_update_notification_controller_
-        ->OnFirmwareUpdateManagerInitialized();
   }
+
   FirmwareUpdateManagerTest(const FirmwareUpdateManagerTest&) = delete;
   FirmwareUpdateManagerTest& operator=(const FirmwareUpdateManagerTest&) =
       delete;
@@ -191,13 +172,21 @@
                       dbus::ObjectProxy::ResponseOrErrorCallback* callback) {
     ASSERT_FALSE(dbus_responses_.empty());
     auto response = std::move(dbus_responses_.front());
-    task_environment_.GetMainThreadTaskRunner()->PostTask(
+    task_environment()->GetMainThreadTaskRunner()->PostTask(
         FROM_HERE, base::BindOnce(&RunResponseCallback, std::move(*callback),
                                   std::move(response)));
     dbus_responses_.pop_front();
   }
 
  protected:
+  void InitializeNotificationController() {
+    firmware_update_notification_controller_ =
+        std::make_unique<FirmwareUpdateNotificationController>(
+            message_center());
+    firmware_update_notification_controller_
+        ->OnFirmwareUpdateManagerInitialized();
+  }
+
   void StartInstall(const std::string& device_id,
                     const base::FilePath& filepath) {
     base::RunLoop loop;
@@ -221,6 +210,10 @@
     firmware_update_manager_->SetFakeUrlForTesting(fake_url);
   }
 
+  message_center::MessageCenter* message_center() const {
+    return message_center::MessageCenter::Get();
+  }
+
   std::unique_ptr<dbus::Response> CreateEmptyDeviceResponse() {
     auto response = dbus::Response::CreateEmpty();
 
@@ -463,7 +456,6 @@
   std::unique_ptr<FwupdClient> dbus_client_;
   std::unique_ptr<FakeFwupdDownloadClient> fake_fwupd_download_client_;
   std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
-  std::unique_ptr<MockMessageCenter> message_center_;
   // `FirmwareUpdateNotificationController` must be be after
   // `FirmwareUpdateManager` to ensure that
   // `FirmwareUpdateNotificationController` is removed as an observer before
@@ -482,7 +474,6 @@
   // Fake responses.
   std::deque<std::unique_ptr<dbus::Response>> dbus_responses_;
 
-  base::test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -790,14 +781,17 @@
 }
 
 TEST_F(FirmwareUpdateManagerTest, NotificationShownForCriticalUpdate) {
+  InitializeNotificationController();
   EXPECT_CALL(*proxy_, DoCallMethodWithErrorResponse(_, _, _))
       .WillRepeatedly(Invoke(this, &FirmwareUpdateManagerTest::OnMethodCalled));
-  EXPECT_EQ(0, message_center_->notification_count());
   dbus_responses_.push_back(CreateOneDeviceResponse());
   dbus_responses_.push_back(CreateOneCriticalUpdateResponse());
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
-  EXPECT_EQ(1, message_center_->notification_count());
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
+  message_center()->RemoveNotification(kFirmwareUpdateNotificationId,
+                                       /*by_user=*/true);
 
   dbus_responses_.push_back(CreateOneDeviceResponse());
   dbus_responses_.push_back(CreateOneCriticalUpdateResponse());
@@ -805,18 +799,22 @@
   base::RunLoop().RunUntilIdle();
   // Request updates again and verify that the notification is not being
   // shown multiple times for the same update.
-  EXPECT_EQ(1, message_center_->notification_count());
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
 }
 
 TEST_F(FirmwareUpdateManagerTest, NotificationNotShownIfNoCriticalUpdates) {
+  InitializeNotificationController();
   EXPECT_CALL(*proxy_, DoCallMethodWithErrorResponse(_, _, _))
       .WillRepeatedly(Invoke(this, &FirmwareUpdateManagerTest::OnMethodCalled));
-  EXPECT_EQ(0, message_center_->notification_count());
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
   dbus_responses_.push_back(CreateOneDeviceResponse());
   dbus_responses_.push_back(CreateOneUpdateResponse());
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
-  EXPECT_EQ(0, message_center_->notification_count());
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
 }
 
 TEST_F(FirmwareUpdateManagerTest, DeviceCountMetric) {
@@ -858,4 +856,90 @@
       "ChromeOS.FirmwareUpdateUi.OnRefresh.UpdateCount", 1, 1);
 }
 
+class FirmwareUpdateStartupNotificationTest : public NoSessionAshTestBase {
+ public:
+  FirmwareUpdateStartupNotificationTest() = default;
+
+  ~FirmwareUpdateStartupNotificationTest() override = default;
+
+  void SetUp() override {
+    dbus_client_ = FwupdClient::Create();
+    firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
+    EXPECT_TRUE(FirmwareUpdateManager::IsInitialized());
+    SetShouldShowNotificationForTest(true);
+    NoSessionAshTestBase::SetUp();
+  }
+
+ protected:
+  void InitializeNotificationController() {
+    firmware_update_notification_controller_ =
+        std::make_unique<FirmwareUpdateNotificationController>(
+            message_center());
+    firmware_update_notification_controller_
+        ->OnFirmwareUpdateManagerInitialized();
+  }
+
+  message_center::MessageCenter* message_center() const {
+    return message_center::MessageCenter::Get();
+  }
+
+  message_center::Notification* FindShortcutsChangedNotification() {
+    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
+        kFirmwareUpdateNotificationId);
+  }
+
+  void SetShouldShowNotificationForTest(bool show_notification) {
+    FirmwareUpdateManager::Get()->set_should_show_notification_for_test(
+        show_notification);
+  }
+
+  void SimulateFetchingUpdates() {
+    FirmwareUpdateManager::Get()->RequestAllUpdates();
+  }
+
+  std::unique_ptr<FwupdClient> dbus_client_;
+  std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
+  std::unique_ptr<FirmwareUpdateNotificationController>
+      firmware_update_notification_controller_;
+};
+
+TEST_F(FirmwareUpdateStartupNotificationTest,
+       StartupNotificationShownRegularUser) {
+  // Notification should be shown at login.
+  SimulateUserLogin("user1@email.com");
+  InitializeNotificationController();
+  SimulateFetchingUpdates();
+  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
+}
+
+TEST_F(FirmwareUpdateStartupNotificationTest,
+       StartupNotificationShownGuestUser) {
+  // Notification should not be shown at login if the user is a guest.
+  SimulateUserLogin("user1@email.com", user_manager::USER_TYPE_GUEST);
+  InitializeNotificationController();
+  SimulateFetchingUpdates();
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
+}
+
+TEST_F(FirmwareUpdateStartupNotificationTest, StartupNotificationShownKiosk) {
+  // Notification should not be shown at login if the user is in kiosk mode.
+  SimulateUserLogin("user1@email.com", user_manager::USER_TYPE_KIOSK_APP);
+  InitializeNotificationController();
+  SimulateFetchingUpdates();
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
+}
+
+TEST_F(FirmwareUpdateStartupNotificationTest,
+       StartupNotificationShownKioskPWA) {
+  // Notification should not be shown at login if the user is in kiosk mode.
+  SimulateUserLogin("user1@email.com", user_manager::USER_TYPE_WEB_KIOSK_APP);
+  InitializeNotificationController();
+  SimulateFetchingUpdates();
+  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
+      kFirmwareUpdateNotificationId));
+}
+
 }  // namespace ash
diff --git a/ash/components/tether/network_configuration_remover.cc b/ash/components/tether/network_configuration_remover.cc
index e1139be..58af15fb 100644
--- a/ash/components/tether/network_configuration_remover.cc
+++ b/ash/components/tether/network_configuration_remover.cc
@@ -5,7 +5,6 @@
 #include "ash/components/tether/network_configuration_remover.h"
 
 #include "base/bind.h"
-#include "base/values.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 
@@ -16,10 +15,8 @@
                   << ".";
 }
 
-void RemoveConfigurationFailureCallback(
-    const std::string& path,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void RemoveConfigurationFailureCallback(const std::string& path,
+                                        const std::string& error_name) {
   PA_LOG(WARNING) << "Failed to remove Wi-Fi network with path " << path
                   << ". Error:" << error_name << ".";
 }
diff --git a/ash/components/tether/network_connection_handler_tether_delegate_unittest.cc b/ash/components/tether/network_connection_handler_tether_delegate_unittest.cc
index 164d69b..d5aa15b 100644
--- a/ash/components/tether/network_connection_handler_tether_delegate_unittest.cc
+++ b/ash/components/tether/network_connection_handler_tether_delegate_unittest.cc
@@ -138,10 +138,7 @@
 
   void OnSuccess() { result_ = kSuccessResult; }
 
-  void OnError(const std::string& error,
-               std::unique_ptr<base::DictionaryValue> error_data) {
-    result_ = error;
-  }
+  void OnError(const std::string& error) { result_ = error; }
 
   std::string GetResultAndReset() {
     std::string result;
diff --git a/ash/components/tether/wifi_hotspot_connector.cc b/ash/components/tether/wifi_hotspot_connector.cc
index faa56397..b8e586e 100644
--- a/ash/components/tether/wifi_hotspot_connector.cc
+++ b/ash/components/tether/wifi_hotspot_connector.cc
@@ -106,9 +106,7 @@
   }
 }
 
-void WifiHotspotConnector::OnEnableWifiError(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void WifiHotspotConnector::OnEnableWifiError(const std::string& error_name) {
   is_waiting_for_wifi_to_enable_ = false;
   PA_LOG(ERROR) << "Failed to enable Wi-Fi: " << error_name;
 }
diff --git a/ash/components/tether/wifi_hotspot_connector.h b/ash/components/tether/wifi_hotspot_connector.h
index 32631c04..ce9df2e 100644
--- a/ash/components/tether/wifi_hotspot_connector.h
+++ b/ash/components/tether/wifi_hotspot_connector.h
@@ -53,8 +53,7 @@
                                     const std::string& tether_network_guid,
                                     WifiConnectionCallback callback);
 
-  void OnEnableWifiError(const std::string& error_name,
-                         std::unique_ptr<base::DictionaryValue> error_data);
+  void OnEnableWifiError(const std::string& error_name);
 
   // NetworkStateHandlerObserver:
   void DeviceListChanged() override;
diff --git a/ash/components/tether/wifi_hotspot_disconnector_impl.cc b/ash/components/tether/wifi_hotspot_disconnector_impl.cc
index a68e6909..a12c0fa 100644
--- a/ash/components/tether/wifi_hotspot_disconnector_impl.cc
+++ b/ash/components/tether/wifi_hotspot_disconnector_impl.cc
@@ -108,8 +108,7 @@
     const std::string& wifi_network_guid,
     const std::string& wifi_network_path,
     StringErrorCallback error_callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   PA_LOG(ERROR) << "Failed to disconnect from Wi-Fi network with GUID "
                 << wifi_network_guid << ". Error name: " << error_name;
   CleanUpAfterWifiDisconnection(wifi_network_path, base::OnceClosure(),
diff --git a/ash/components/tether/wifi_hotspot_disconnector_impl.h b/ash/components/tether/wifi_hotspot_disconnector_impl.h
index 108e41ba..82f2f00 100644
--- a/ash/components/tether/wifi_hotspot_disconnector_impl.h
+++ b/ash/components/tether/wifi_hotspot_disconnector_impl.h
@@ -49,12 +49,10 @@
                                   const std::string& wifi_network_path,
                                   base::OnceClosure success_callback,
                                   StringErrorCallback error_callback);
-  void OnFailedWifiDisconnect(
-      const std::string& wifi_network_guid,
-      const std::string& wifi_network_path,
-      StringErrorCallback error_callback,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void OnFailedWifiDisconnect(const std::string& wifi_network_guid,
+                              const std::string& wifi_network_path,
+                              StringErrorCallback error_callback,
+                              const std::string& error_name);
   void CleanUpAfterWifiDisconnection(const std::string& wifi_network_path,
                                      base::OnceClosure success_callback,
                                      StringErrorCallback error_callback);
diff --git a/ash/components/tether/wifi_hotspot_disconnector_impl_unittest.cc b/ash/components/tether/wifi_hotspot_disconnector_impl_unittest.cc
index ec9acd5..bdfc59f 100644
--- a/ash/components/tether/wifi_hotspot_disconnector_impl_unittest.cc
+++ b/ash/components/tether/wifi_hotspot_disconnector_impl_unittest.cc
@@ -181,8 +181,7 @@
       network_handler::RunErrorCallback(
           std::move(test_network_connection_handler_
                         ->last_disconnect_error_callback()),
-          wifi_service_path_, NetworkConnectionHandler::kErrorDisconnectFailed,
-          std::string() /* error_detail */);
+          NetworkConnectionHandler::kErrorDisconnectFailed);
     }
 
     // Now that the callbacks have been invoked, both the network
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index d261b14..b6f3253 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -863,6 +863,11 @@
 const base::Feature kLauncherAppSort{"LauncherAppSort",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
+// When enabled, app list folders will be moved so app list remains sorted when
+// they get renamed, or created.
+const base::Feature kLauncherFolderRenameKeepsSortOrder{
+    "LauncherFolderRenameKeepsSortOrder", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Uses short intervals for launcher nudge for testing if enabled.
 const base::Feature kLauncherNudgeShortInterval{
     "LauncherNudgeShortInterval", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -1667,6 +1672,11 @@
          base::FeatureList::IsEnabled(kLauncherAppSort);
 }
 
+bool IsLauncherFolderRenameKeepsSortOrderEnabled() {
+  return IsLauncherAppSortEnabled() &&
+         base::FeatureList::IsEnabled(kLauncherFolderRenameKeepsSortOrder);
+}
+
 bool IsLauncherNudgeShortIntervalEnabled() {
   return IsProductivityLauncherEnabled() &&
          base::FeatureList::IsEnabled(kLauncherNudgeShortInterval);
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index fd2e5f0..8df8130 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -324,6 +324,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLauncherAppSort;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kLauncherFolderRenameKeepsSortOrder;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLauncherNudgeShortInterval;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLicensePackagedOobeFlow;
@@ -595,6 +597,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsKeyboardBasedDisplayArrangementInSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsLauncherAppSortEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS)
+bool IsLauncherFolderRenameKeepsSortOrderEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsLauncherNudgeShortIntervalEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsLicensePackagedOobeFlowEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/public/cpp/app_list/app_list_model_delegate.h b/ash/public/cpp/app_list/app_list_model_delegate.h
index 5429854..ae1629f5 100644
--- a/ash/public/cpp/app_list/app_list_model_delegate.h
+++ b/ash/public/cpp/app_list/app_list_model_delegate.h
@@ -31,8 +31,7 @@
   // Requests the owner to move the item indexed by `id` into the specified
   // folder.
   virtual void RequestMoveItemToFolder(std::string id,
-                                       const std::string& folder_id,
-                                       RequestMoveToFolderReason reason) = 0;
+                                       const std::string& folder_id) = 0;
 
   // Requests the owner to move the item indexed by `id` out of its parent
   // folder. `target_position` is the item position after move.
@@ -46,6 +45,17 @@
   virtual void RequestMoveItemToRoot(std::string id,
                                      syncer::StringOrdinal target_position) = 0;
 
+  // Requests a folder creation by merging two app list items with provided app
+  // IDs. `item_to_merge_id` will be merged into `merge_target_id`.
+  // Returns the ID of the created folder item.
+  virtual std::string RequestFolderCreation(std::string merge_target_id,
+                                            std::string item_to_merge_id) = 0;
+
+  // Requests a folder item that has identified by `folder_id` to be renamed to
+  // `new_name`.
+  virtual void RequestFolderRename(std::string folder_id,
+                                   const std::string& new_name) = 0;
+
   // Invoked when app list sort is requested.
   virtual void RequestAppListSort(AppListSortOrder order) = 0;
 
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index d2bf4ad..7e78033 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -170,18 +170,6 @@
   kMoveItem
 };
 
-// Lists the reasons that ash requests to move an item into a folder.
-enum class RequestMoveToFolderReason {
-  // Merge two items and move the first item to the created folder.
-  kMergeFirstItem,
-
-  // Merge two items and move the second item to the created folder.
-  kMergeSecondItem,
-
-  // Move an item to an existed folder.
-  kMoveItem
-};
-
 // All possible states of the app list.
 // Note: Do not change the order of these as they are used for metrics.
 enum class AppListState {
diff --git a/ash/public/cpp/resources/unscaled_resources/dictation_bubble_animation.json b/ash/public/cpp/resources/unscaled_resources/dictation_bubble_animation.json
index 62d1ac6..33511ab 100644
--- a/ash/public/cpp/resources/unscaled_resources/dictation_bubble_animation.json
+++ b/ash/public/cpp/resources/unscaled_resources/dictation_bubble_animation.json
@@ -1 +1 @@
-{"assets":[],"ddd":0,"fr":29.9700012207031,"h":256,"ip":0,"layers":[{"ao":0,"bm":0,"ddd":0,"ind":1,"ip":0,"ks":{"a":{"a":0,"k":[-70,-0.5,0]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"e":[208.6,88,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[208.6,127.969,0],"t":20,"ti":[0,-0.00520833348855,0],"to":[0,-6.66145849227905,0]},{"e":[208.6,128,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[208.6,88,0],"t":30,"ti":[0,-6.66666650772095,0],"to":[0,0.00520833348855,0]},{"t":40.0000016292334}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[75,75,100]}},"nm":"Shape Layer 3","op":300.00001221925,"shapes":[{"cix":2,"it":[{"d":1,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[33.75,34.5]},"ty":"el"},{"c":{"a":0,"k":[0.9843137,0.5490196,0,1]},"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[-70.125,-0.5]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Ellipse 1","np":3,"ty":"gr"}],"sr":1,"st":0,"ty":4},{"ao":0,"bm":0,"ddd":0,"ind":2,"ip":0,"ks":{"a":{"a":0,"k":[-70,-0.5,0]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"e":[168.6,88,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[168.6,128,0],"t":15,"ti":[0,0,0],"to":[0,-6.66666650772095,0]},{"e":[168.6,128,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[168.6,88,0],"t":25,"ti":[0,-6.66666650772095,0],"to":[0,0,0]},{"t":35.0000014255792}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[75,75,100]}},"nm":"Shape Layer 2","op":300.00001221925,"shapes":[{"cix":2,"it":[{"d":1,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[33.75,34.5]},"ty":"el"},{"c":{"a":0,"k":[0.9921569,0.8470588,0.2078431,1]},"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[-70.125,-0.5]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Ellipse 1","np":3,"ty":"gr"}],"sr":1,"st":0,"ty":4},{"ao":0,"bm":0,"ddd":0,"ind":3,"ip":0,"ks":{"a":{"a":0,"k":[-70,-0.5,0]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"e":[128.594,88,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[128.594,127.969,0],"t":10,"ti":[0,-0.00520833348855,0],"to":[0,-6.66145849227905,0]},{"e":[128.594,128,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[128.594,88,0],"t":20,"ti":[0,-6.66666650772095,0],"to":[0,0.00520833348855,0]},{"t":30.0000012219251}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[75,75,100]}},"nm":"Shape Layer 1","op":300.00001221925,"shapes":[{"cix":2,"it":[{"d":1,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[33.75,34.5]},"ty":"el"},{"c":{"a":0,"k":[0.2627451,0.627451,0.2784314,1]},"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[-70.125,-0.5]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Ellipse 1","np":3,"ty":"gr"}],"sr":1,"st":0,"ty":4},{"ao":0,"bm":0,"ddd":0,"ind":4,"ip":0,"ks":{"a":{"a":0,"k":[-70,-0.5,0]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"e":[88.6,88,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[88.6,127.969,0],"t":5,"ti":[0,-0.00520833348855,0],"to":[0,-6.66145849227905,0]},{"e":[88.6,128,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[88.6,88,0],"t":15,"ti":[0,-6.66666650772095,0],"to":[0,0.00520833348855,0]},{"t":25.0000010182709}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[75,75,100]}},"nm":"Shape Layer 4","op":300.00001221925,"shapes":[{"cix":2,"it":[{"d":1,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[33.75,34.5]},"ty":"el"},{"c":{"a":0,"k":[0.1176471,0.5333334,0.8980392,1]},"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[-70.125,-0.5]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Ellipse 1","np":3,"ty":"gr"}],"sr":1,"st":0,"ty":4},{"ao":0,"bm":0,"ddd":0,"ind":5,"ip":0,"ks":{"a":{"a":0,"k":[-70,-0.5,0]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"e":[48.6,88,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[48.6,127.969,0],"t":0,"ti":[0,-0.00520833348855,0],"to":[0,-6.66145849227905,0]},{"e":[48.6,128,0],"i":{"x":0.667,"y":1},"n":"0p667_1_0p333_0","o":{"x":0.333,"y":0},"s":[48.6,88,0],"t":10,"ti":[0,-6.66666650772095,0],"to":[0,0.00520833348855,0]},{"t":20.0000008146167}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[75,75,100]}},"nm":"Shape Layer 5","op":300.00001221925,"shapes":[{"cix":2,"it":[{"d":1,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[33.75,34.5]},"ty":"el"},{"c":{"a":0,"k":[0.8980392,0.2235294,0.2078431,1]},"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[-70.125,-0.5]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Ellipse 1","np":3,"ty":"gr"}],"sr":1,"st":0,"ty":4}],"nm":"Comp 1","op":40.0000016292334,"v":"4.6.8","w":256}
\ No newline at end of file
+{"v":"5.8.1","fr":60,"ip":709,"op":771,"w":23,"h":23,"nm":"equalizer 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[20.562,13.125,0],"ix":2,"l":2},"a":{"a":0,"k":[19.812,7.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":714,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":717,"s":[100,290,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":720,"s":[100,275,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":726,"s":[100,40,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":729,"s":[100,113,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,0.111,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":735,"s":[100,50,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":743,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":749,"s":[100,80,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,0,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":762,"s":[100,50,100]},{"t":771,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.828,0],[0,0],[0,-0.828],[0,0],[0.828,0],[0,0.828],[0,0]],"o":[[0,0],[0.828,0],[0,0],[0,0.828],[-0.828,0],[0,0],[0,-0.828]],"v":[[0.004,-2.542],[0.011,-2.543],[1.504,-1.042],[1.5,1.41],[0,2.91],[-1.5,1.41],[-1.496,-1.042]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078491211,0.980392216701,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[19.75,7.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1769,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[14.688,13.125,0],"ix":2,"l":2},"a":{"a":0,"k":[13.938,7.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":711,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":714,"s":[100,50,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":717,"s":[100,40,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":723,"s":[100,120,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":728,"s":[100,56,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,7.209,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":734,"s":[100,115,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":743,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":746,"s":[100,116,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":752,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":758,"s":[100,48,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,-24.869,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":761,"s":[100,96,100]},{"t":771,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.828,0],[0,0],[0,-0.828],[0,0],[0.828,0],[0,0.828],[0,0]],"o":[[0,0],[0.828,0],[0,0],[0,0.828],[-0.828,0],[0,0],[0,-0.828]],"v":[[0,-7],[0,-7],[1.5,-5.5],[1.5,5.5],[0,7],[-1.5,5.5],[-1.5,-5.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078491211,0.980392216701,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.75,7.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1769,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[8.594,13.125,0],"ix":2,"l":2},"a":{"a":0,"k":[7.844,7.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":711,"s":[100,100,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,-18.66,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":734,"s":[100,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":743,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":746,"s":[100,150,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":755,"s":[100,40,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,-1.664,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":761,"s":[100,59,100]},{"t":771,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.828,0],[0,0],[0,-0.828],[0,0],[0.828,0],[0,0.828],[0,0]],"o":[[0,0],[0.828,0],[0,0],[0,0.828],[-0.828,0],[0,0],[0,-0.828]],"v":[[0,-4],[0,-4],[1.5,-2.5],[1.5,2.5],[0,4],[-1.5,2.5],[-1.5,-2.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078491211,0.980392216701,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.75,7.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1769,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2.594,13.125,0],"ix":2,"l":2},"a":{"a":0,"k":[1.844,7.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":709,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":712,"s":[100,80,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,0,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":734,"s":[100,33,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":743,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":752,"s":[100,40,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":756,"s":[100,100,100]},{"i":{"x":[0.86,0.86,0.86],"y":[1,0.1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":762,"s":[100,50,100]},{"t":771,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.828,0],[0,0],[0,-0.828],[0,0],[0.828,0],[0,0.828],[0,0]],"o":[[0,0],[0.828,0],[0,0],[0,0.828],[-0.828,0],[0,0],[0,-0.828]],"v":[[0,-6],[0,-6],[1.5,-4.5],[1.5,4.5],[0,6],[-1.5,4.5],[-1.5,-4.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.682352941176,0.796078491211,0.980392216701,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.75,7.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1769,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.cc b/ash/quick_pair/repository/fake_fast_pair_repository.cc
index 5f00e30..c2e8ac0 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.cc
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.cc
@@ -43,6 +43,11 @@
 void FakeFastPairRepository::GetDeviceMetadata(
     const std::string& hex_model_id,
     DeviceMetadataCallback callback) {
+  if (!is_network_connected_) {
+    std::move(callback).Run(/*device=*/nullptr, /*has_retryable_error=*/true);
+    return;
+  }
+
   std::string normalized_id = base::ToUpperASCII(hex_model_id);
   if (data_.contains(normalized_id)) {
     std::move(callback).Run(data_[normalized_id].get(),
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.h b/ash/quick_pair/repository/fake_fast_pair_repository.h
index eb911efe..9c1fc0ed 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.h
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.h
@@ -45,6 +45,10 @@
 
   bool HasKeyForDevice(const std::string& mac_address);
 
+  void set_is_network_connected(bool is_connected) {
+    is_network_connected_ = is_connected;
+  }
+
   // FastPairRepository::
   void GetDeviceMetadata(const std::string& hex_model_id,
                          DeviceMetadataCallback callback) override;
@@ -64,6 +68,7 @@
  private:
   static void SetInstance(FastPairRepository* instance);
 
+  bool is_network_connected_ = true;
   base::flat_map<std::string, std::unique_ptr<DeviceMetadata>> data_;
   base::flat_map<std::string, std::vector<uint8_t>> saved_account_keys_;
   absl::optional<PairingMetadata> check_account_key_result_;
diff --git a/ash/quick_pair/scanning/BUILD.gn b/ash/quick_pair/scanning/BUILD.gn
index 238e174..c3cc291f 100644
--- a/ash/quick_pair/scanning/BUILD.gn
+++ b/ash/quick_pair/scanning/BUILD.gn
@@ -29,6 +29,7 @@
     "//ash/services/quick_pair",
     "//ash/services/quick_pair/public/cpp",
     "//base",
+    "//chromeos/network",
     "//device/bluetooth",
     "//device/bluetooth/public/cpp",
   ]
@@ -76,6 +77,8 @@
     "//ash/services/quick_pair",
     "//ash/services/quick_pair:test_support",
     "//base/test:test_support",
+    "//chromeos/network:test_support",
+    "//dbus:test_support",
     "//device/bluetooth",
     "//device/bluetooth:mocks",
     "//mojo/public/cpp/bindings",
diff --git a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.cc b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.cc
index 2c99c49e..4fd27eea 100644
--- a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.cc
+++ b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.cc
@@ -25,6 +25,8 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_util.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
 #include "device/bluetooth//bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -49,9 +51,14 @@
       found_callback_(std::move(found_callback)),
       lost_callback_(std::move(lost_callback)) {
   observation_.Observe(scanner_.get());
+  chromeos::NetworkHandler::Get()->network_state_handler()->AddObserver(
+      this, FROM_HERE);
 }
 
-FastPairDiscoverableScanner::~FastPairDiscoverableScanner() = default;
+FastPairDiscoverableScanner::~FastPairDiscoverableScanner() {
+  chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+      this, FROM_HERE);
+}
 
 void FastPairDiscoverableScanner::OnDeviceFound(
     device::BluetoothDevice* device) {
@@ -116,18 +123,26 @@
     const std::string model_id,
     DeviceMetadata* device_metadata,
     bool has_retryable_error) {
+  if (has_retryable_error) {
+    pending_devices_address_to_model_id_[address] = model_id;
+    QP_LOG(WARNING) << __func__
+                    << ": Could not retrieve metadata for id: " << model_id
+                    << ". Waiting for network change before retry.";
+    return;
+  }
+
   if (!device_metadata) {
     QP_LOG(WARNING) << __func__
-                    << ": Could not get metadata for id: " << model_id
+                    << ": No metadata available for id: " << model_id
                     << ". Ignoring this advertisement";
     return;
   }
 
-  QP_LOG(VERBOSE) << __func__ << ": Id: " << model_id;
-
   auto device = base::MakeRefCounted<Device>(model_id, address,
                                              Protocol::kFastPairInitial);
 
+  QP_LOG(VERBOSE) << __func__ << ": Id: " << model_id;
+
   // Anti-spoofing keys were introduced in Fast Pair v2, so if this isn't
   // available then the device is v1.
   if (device_metadata->GetDetails()
@@ -182,6 +197,9 @@
     device::BluetoothDevice* device) {
   QP_LOG(VERBOSE) << __func__ << ": " << device->GetNameForDisplay();
 
+  // No need to retry fetching metadata for devices that are no longer in range.
+  pending_devices_address_to_model_id_.erase(device->GetAddress());
+
   // If we have an in-progress attempt to parse for this device, removing it
   // from this map will ensure the result is ignored.
   model_id_parse_attempts_.erase(device->GetAddress());
@@ -231,5 +249,25 @@
                      weak_pointer_factory_.GetWeakPtr(), address));
 }
 
+void FastPairDiscoverableScanner::DefaultNetworkChanged(
+    const chromeos::NetworkState* network) {
+  // Only retry when we have an active connected network.
+  if (!network || !network->IsConnectedState()) {
+    return;
+  }
+
+  auto it = pending_devices_address_to_model_id_.begin();
+  while (it != pending_devices_address_to_model_id_.end()) {
+    FastPairRepository::Get()->GetDeviceMetadata(
+        /*model_id=*/it->second,
+        base::BindOnce(&FastPairDiscoverableScanner::OnDeviceMetadataRetrieved,
+                       weak_pointer_factory_.GetWeakPtr(),
+                       /*address=*/it->first,
+                       /*model_id=*/it->second));
+
+    pending_devices_address_to_model_id_.erase(it);
+  }
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.h b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.h
index edc1769..ad5eff0 100644
--- a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.h
+++ b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.h
@@ -14,6 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "chromeos/network/network_state_handler_observer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
@@ -35,7 +36,9 @@
 // and invokes the |found_callback| when it finds a device within the
 // appropriate range.  |lost_callback| will be invoked when that device is lost
 // to the bluetooth adapter.
-class FastPairDiscoverableScanner final : public FastPairScanner::Observer {
+class FastPairDiscoverableScanner final
+    : public FastPairScanner::Observer,
+      public chromeos::NetworkStateHandlerObserver {
  public:
   FastPairDiscoverableScanner(scoped_refptr<FastPairScanner> scanner,
                               scoped_refptr<device::BluetoothAdapter> adapter,
@@ -50,6 +53,9 @@
   void OnDeviceFound(device::BluetoothDevice* device) override;
   void OnDeviceLost(device::BluetoothDevice* device) override;
 
+  // chromeos::NetworkStateHandlerObserver:
+  void DefaultNetworkChanged(const chromeos::NetworkState* network) override;
+
  private:
   void OnModelIdRetrieved(const std::string& address,
                           const absl::optional<std::string>& model_id);
@@ -68,6 +74,7 @@
   scoped_refptr<device::BluetoothAdapter> adapter_;
   DeviceCallback found_callback_;
   DeviceCallback lost_callback_;
+  base::flat_map<std::string, std::string> pending_devices_address_to_model_id_;
   base::flat_map<std::string, scoped_refptr<Device>> notified_devices_;
   base::flat_map<std::string, int> model_id_parse_attempts_;
   base::ScopedObservation<FastPairScanner, FastPairScanner::Observer>
diff --git a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner_unittest.cc b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner_unittest.cc
index dedcaf5d..3194e92 100644
--- a/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner_unittest.cc
+++ b/ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner_unittest.cc
@@ -27,6 +27,9 @@
 #include "base/test/bind.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_test_helper.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -45,6 +48,7 @@
 class FastPairDiscoverableScannerTest : public testing::Test {
  public:
   void SetUp() override {
+    chromeos::NetworkHandler::Initialize();
     repository_ = std::make_unique<FakeFastPairRepository>();
 
     nearby::fastpair::Device metadata;
@@ -81,6 +85,12 @@
         lost_device_callback_.Get());
   }
 
+  void TearDown() override {
+    testing::Test::TearDown();
+    discoverable_scanner_.reset();
+    chromeos::NetworkHandler::Shutdown();
+  }
+
   MockQuickPairProcessManager* mock_process_manager() {
     return static_cast<MockQuickPairProcessManager*>(process_manager_.get());
   }
@@ -119,6 +129,7 @@
   }
 
   base::test::SingleThreadTaskEnvironment task_enviornment_;
+  chromeos::NetworkStateTestHelper helper_{/*use_defaults=*/true};
   scoped_refptr<FakeFastPairScanner> scanner_;
   std::unique_ptr<FakeFastPairRepository> repository_;
   std::unique_ptr<FastPairDiscoverableScanner> discoverable_scanner_;
@@ -179,6 +190,38 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(FastPairDiscoverableScannerTest,
+       InvokesFoundCallback_AfterNetworkAvailable) {
+  device::BluetoothDevice* device = GetDevice(kValidModelId);
+  repository_->set_is_network_connected(false);
+
+  EXPECT_CALL(found_device_callback_, Run).Times(0);
+  scanner_->NotifyDeviceFound(device);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(found_device_callback_, Run).Times(1);
+  repository_->set_is_network_connected(true);
+  discoverable_scanner_->DefaultNetworkChanged(
+      helper_.network_state_handler()->DefaultNetwork());
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(FastPairDiscoverableScannerTest,
+       NoFoundCallback_AfterDeviceLostAndNetworkAvailable) {
+  device::BluetoothDevice* device = GetDevice(kValidModelId);
+  repository_->set_is_network_connected(false);
+
+  EXPECT_CALL(found_device_callback_, Run).Times(0);
+  scanner_->NotifyDeviceFound(device);
+  base::RunLoop().RunUntilIdle();
+
+  scanner_->NotifyDeviceLost(device);
+  repository_->set_is_network_connected(true);
+  discoverable_scanner_->DefaultNetworkChanged(
+      helper_.network_state_handler()->DefaultNetwork());
+  base::RunLoop().RunUntilIdle();
+}
+
 TEST_F(FastPairDiscoverableScannerTest, InvokesLostCallbackAfterFound_v2) {
   nearby::fastpair::Device metadata;
   metadata.set_trigger_distance(2);
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index 7eb02b8..5fb4c36 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -152,8 +152,9 @@
                          : IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE;
     AddItemWithStringIdAndIcon(
         MENU_AUTO_HIDE, string_id,
-        ui::ImageModel::FromVectorIcon(is_autohide_set ? kAlwaysShowShelfIcon
-                                                       : kAutoHideIcon));
+        ui::ImageModel::FromVectorIcon(
+            is_autohide_set ? kAlwaysShowShelfIcon : kAutoHideIcon,
+            ui::kColorAshSystemUIMenuIcon));
   }
 
   // Only allow shelf alignment modifications by the logged in Gaia users
@@ -176,17 +177,20 @@
     AddSubMenuWithStringIdAndIcon(
         MENU_ALIGNMENT_MENU, IDS_ASH_SHELF_CONTEXT_MENU_POSITION,
         alignment_submenu_.get(),
-        ui::ImageModel::FromVectorIcon(kShelfPositionIcon));
+        ui::ImageModel::FromVectorIcon(kShelfPositionIcon,
+                                       ui::kColorAshSystemUIMenuIcon));
   }
 
   if (ash::features::IsPersonalizationHubEnabled()) {
-    AddItemWithStringIdAndIcon(MENU_PERSONALIZATION_HUB,
-                               IDS_AURA_OPEN_PERSONALIZATION_HUB,
-                               ui::ImageModel::FromVectorIcon(kPaintBrushIcon));
+    AddItemWithStringIdAndIcon(
+        MENU_PERSONALIZATION_HUB, IDS_AURA_OPEN_PERSONALIZATION_HUB,
+        ui::ImageModel::FromVectorIcon(kPaintBrushIcon,
+                                       ui::kColorAshSystemUIMenuIcon));
   } else if (Shell::Get()->wallpaper_controller()->CanOpenWallpaperPicker()) {
-    AddItemWithStringIdAndIcon(MENU_CHANGE_WALLPAPER,
-                               IDS_AURA_SET_DESKTOP_WALLPAPER,
-                               ui::ImageModel::FromVectorIcon(kWallpaperIcon));
+    AddItemWithStringIdAndIcon(
+        MENU_CHANGE_WALLPAPER, IDS_AURA_SET_DESKTOP_WALLPAPER,
+        ui::ImageModel::FromVectorIcon(kWallpaperIcon,
+                                       ui::kColorAshSystemUIMenuIcon));
   }
 }
 
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index 158efd1..aebe13f 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -67,7 +67,8 @@
   // Show a default context menu with just an extra close item.
   menu->AddItemWithStringIdAndIcon(
       kCloseCommandId, IDS_CLOSE,
-      ui::ImageModel::FromVectorIcon(views::kCloseIcon));
+      ui::ImageModel::FromVectorIcon(views::kCloseIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
   std::move(callback).Run(std::move(menu));
 }
 
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index 339733b..c2f26b0 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -18,11 +18,21 @@
   if (!features::IsDarkLightModeEnabled())
     return;
 
-  const SkColor menu_background_color =
-      AshColorProvider::Get()->GetBaseLayerColor(
-          AshColorProvider::BaseLayerType::kTransparent80);
+  auto* ash_color_provider = AshColorProvider::Get();
   ui::ColorMixer& mixer = provider->AddMixer();
-  mixer[ui::kColorAshSystemUIMenuBackground] = {menu_background_color};
+  mixer[ui::kColorAshSystemUIMenuBackground] = {
+      ash_color_provider->GetBaseLayerColor(
+          AshColorProvider::BaseLayerType::kTransparent80)};
+  mixer[ui::kColorAshSystemUIMenuIcon] = {
+      ash_color_provider->GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kIconColorPrimary)};
+
+  auto [color, opacity] = ash_color_provider->GetInkDropBaseColorAndOpacity();
+  mixer[ui::kColorAshSystemUIMenuItemBackgroundSelected] = {
+      SkColorSetA(color, opacity * SK_AlphaOPAQUE)};
+  mixer[ui::kColorAshSystemUIMenuSeparator] = {
+      ash_color_provider->GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kSeparatorColor)};
 }
 
 }  // namespace ash
diff --git a/ash/system/accessibility/dictation_bubble_view.cc b/ash/system/accessibility/dictation_bubble_view.cc
index 86e9627f..5c2e94ea 100644
--- a/ash/system/accessibility/dictation_bubble_view.cc
+++ b/ash/system/accessibility/dictation_bubble_view.cc
@@ -234,6 +234,7 @@
       num_visible_hints = hints.value().size();
     }
 
+    // Update labels.
     for (size_t i = 0; i < labels_.size(); ++i) {
       bool has_hint_for_index = hints.has_value() && (i < hints.value().size());
       labels_[i]->SetVisible(has_hint_for_index);
@@ -246,8 +247,16 @@
     }
 
     // Set visibility of this view based on the number of visible hints.
-    SetVisible(num_visible_hints > 0 ? true : false);
-
+    // If the hint view is visible, send an alert event so that ChromeVox reads
+    // hints to the user.
+    if (num_visible_hints > 0) {
+      SetVisible(true);
+      // TODO(crbug.com/1252037): Write a DictationUITest to verify that
+      // ChromeVox announces hints.
+      NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
+    } else {
+      SetVisible(false);
+    }
     SizeToPreferredSize();
   }
 
diff --git a/ash/system/firmware_update/firmware_update_notification_controller.cc b/ash/system/firmware_update/firmware_update_notification_controller.cc
index ada2771b..71c8b992 100644
--- a/ash/system/firmware_update/firmware_update_notification_controller.cc
+++ b/ash/system/firmware_update/firmware_update_notification_controller.cc
@@ -8,10 +8,12 @@
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/model/system_tray_model.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/user_manager/user_type.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/message_center.h"
@@ -56,6 +58,28 @@
   RemoveNotification(kFirmwareUpdateNotificationId);
 }
 
+bool ShouldShowNotification() {
+  const absl::optional<user_manager::UserType> user_type =
+      Shell::Get()->session_controller()->GetUserType();
+  if (!user_type) {
+    return false;
+  }
+
+  switch (*user_type) {
+    case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+    case user_manager::USER_TYPE_GUEST:
+    case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+    case user_manager::USER_TYPE_KIOSK_APP:
+    case user_manager::USER_TYPE_ARC_KIOSK_APP:
+    case user_manager::USER_TYPE_WEB_KIOSK_APP:
+    case user_manager::NUM_USER_TYPES:
+      return false;
+    case user_manager::USER_TYPE_REGULAR:
+    case user_manager::USER_TYPE_CHILD:
+      return true;
+  }
+}
+
 }  // namespace
 
 FirmwareUpdateNotificationController::FirmwareUpdateNotificationController(
@@ -104,7 +128,9 @@
 }
 
 void FirmwareUpdateNotificationController::OnFirmwareUpdateReceived() {
-  NotifyFirmwareUpdateAvailable();
+  if (ShouldShowNotification()) {
+    NotifyFirmwareUpdateAvailable();
+  }
 }
 
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/holding_space/holding_space_animation_registry.cc b/ash/system/holding_space/holding_space_animation_registry.cc
index 4f8785c..4a607bf 100644
--- a/ash/system/holding_space/holding_space_animation_registry.cc
+++ b/ash/system/holding_space/holding_space_animation_registry.cc
@@ -13,8 +13,8 @@
 #include "ash/public/cpp/holding_space/holding_space_controller_observer.h"
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase_map.h"
 #include "base/memory/ptr_util.h"
@@ -101,8 +101,8 @@
     // If `item` has just progressed to completion, ensure that a pulse
     // animation is created and started.
     if (item->progress().IsComplete()) {
-      EnsureRingAnimationOfTypeForKey(
-          item, HoldingSpaceProgressRingAnimation::Type::kPulse);
+      EnsureRingAnimationOfTypeForKey(item,
+                                      ProgressRingAnimation::Type::kPulse);
     }
 
     UpdateAnimations(/*for_removal=*/false);
@@ -110,9 +110,8 @@
 
   // Erases the ring animation for the specified `key` if it is not of the
   // desired `type`, notifying any animation changed callbacks.
-  void EraseRingAnimationIfNotOfTypeForKey(
-      const void* key,
-      HoldingSpaceProgressRingAnimation::Type type) {
+  void EraseRingAnimationIfNotOfTypeForKey(const void* key,
+                                           ProgressRingAnimation::Type type) {
     auto* ring_animation = registry_->GetProgressRingAnimationForKey(key);
     if (ring_animation && ring_animation->type() != type)
       registry_->SetProgressRingAnimationForKey(key, nullptr);
@@ -131,21 +130,20 @@
 
     registry_
         ->SetProgressIconAnimationForKey(
-            key, std::make_unique<HoldingSpaceProgressIconAnimation>())
+            key, std::make_unique<ProgressIconAnimation>())
         ->Start();
   }
 
   // Ensures that the ring animation for the specified `key` is of the desired
   // `type`. If necessary, a new animation is created and started, notifying any
   // animation changed callbacks.
-  void EnsureRingAnimationOfTypeForKey(
-      const void* key,
-      HoldingSpaceProgressRingAnimation::Type type) {
+  void EnsureRingAnimationOfTypeForKey(const void* key,
+                                       ProgressRingAnimation::Type type) {
     auto* ring_animation = registry_->GetProgressRingAnimationForKey(key);
     if (ring_animation && ring_animation->type() == type)
       return;
 
-    auto animation = HoldingSpaceProgressRingAnimation::CreateOfType(type);
+    auto animation = ProgressRingAnimation::CreateOfType(type);
     animation->AddUnsafeAnimationUpdatedCallback(base::BindRepeating(
         &ProgressIndicatorAnimationDelegate::OnRingAnimationUpdatedForKey,
         base::Unretained(this), key, animation.get()));
@@ -197,7 +195,7 @@
       if (item->progress().IsComplete()) {
         registry_->SetProgressIconAnimationForKey(item.get(), nullptr);
         EraseRingAnimationIfNotOfTypeForKey(
-            item.get(), HoldingSpaceProgressRingAnimation::Type::kPulse);
+            item.get(), ProgressRingAnimation::Type::kPulse);
         continue;
       }
 
@@ -211,8 +209,7 @@
       // should be associated with it (if one does not already exist).
       if (item->progress().IsIndeterminate()) {
         EnsureRingAnimationOfTypeForKey(
-            item.get(),
-            HoldingSpaceProgressRingAnimation::Type::kIndeterminate);
+            item.get(), ProgressRingAnimation::Type::kIndeterminate);
         continue;
       }
 
@@ -236,15 +233,15 @@
           // If `cumulative_progress_` has just become complete and is *not* due
           // to the removal of one or more holding space items, ensure that a
           // pulse animation is created and started.
-          EnsureRingAnimationOfTypeForKey(
-              controller_, HoldingSpaceProgressRingAnimation::Type::kPulse);
+          EnsureRingAnimationOfTypeForKey(controller_,
+                                          ProgressRingAnimation::Type::kPulse);
         }
       } else {
         // If `cumulative_progress_` was already complete, it should be allowed
         // to continue a pulse animation if one was previously created and
         // started. Any other type of ring animation should be cleared.
         EraseRingAnimationIfNotOfTypeForKey(
-            controller_, HoldingSpaceProgressRingAnimation::Type::kPulse);
+            controller_, ProgressRingAnimation::Type::kPulse);
       }
       return;
     }
@@ -258,7 +255,7 @@
     // already exist).
     if (cumulative_progress_.IsIndeterminate()) {
       EnsureRingAnimationOfTypeForKey(
-          controller_, HoldingSpaceProgressRingAnimation::Type::kIndeterminate);
+          controller_, ProgressRingAnimation::Type::kIndeterminate);
       return;
     }
 
@@ -269,9 +266,8 @@
 
   // Invoked when the specified ring `animation` for the specified `key` has
   // been updated. This is used to clean up finished animations.
-  void OnRingAnimationUpdatedForKey(
-      const void* key,
-      HoldingSpaceProgressRingAnimation* animation) {
+  void OnRingAnimationUpdatedForKey(const void* key,
+                                    ProgressRingAnimation* animation) {
     if (animation->IsAnimating())
       return;
     // Once `animation` has finished, it can be removed from the registry. Note
@@ -282,7 +278,7 @@
         base::BindOnce(
             [](const base::WeakPtr<ProgressIndicatorAnimationDelegate>&
                    delegate,
-               const void* key, HoldingSpaceProgressRingAnimation* animation) {
+               const void* key, ProgressRingAnimation* animation) {
               if (!delegate)
                 return;
               auto* registry = delegate->registry_;
diff --git a/ash/system/holding_space/holding_space_animation_registry_unittest.cc b/ash/system/holding_space/holding_space_animation_registry_unittest.cc
index 74377bd..e18a387 100644
--- a/ash/system/holding_space/holding_space_animation_registry_unittest.cc
+++ b/ash/system/holding_space/holding_space_animation_registry_unittest.cc
@@ -15,9 +15,9 @@
 #include "ash/public/cpp/holding_space/holding_space_prefs.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "ash/public/cpp/holding_space/mock_holding_space_client.h"
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
-#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
+#include "ash/system/progress_indicator/progress_indicator_animation.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "ash/test/ash_test_base.h"
 #include "base/barrier_closure.h"
 #include "base/strings/strcat.h"
@@ -90,7 +90,7 @@
 
   void ExpectProgressRingAnimationOfTypeForKey(
       const void* key,
-      const absl::optional<HoldingSpaceProgressRingAnimation::Type>& type) {
+      const absl::optional<ProgressRingAnimation::Type>& type) {
     auto* animation = registry()->GetProgressRingAnimationForKey(key);
     EXPECT_EQ(!!animation, type.has_value());
     if (animation && type.has_value())
@@ -129,7 +129,7 @@
 // Tests -----------------------------------------------------------------------
 
 TEST_P(HoldingSpaceAnimationRegistryTest, ProgressIndicatorAnimations) {
-  using Type = HoldingSpaceProgressRingAnimation::Type;
+  using Type = ProgressRingAnimation::Type;
 
   StartSession();
 
@@ -220,14 +220,14 @@
 
     std::array<base::CallbackListSubscription, 3u> subscriptions = {
         registry()->AddProgressRingAnimationChangedCallbackForKey(
-            controller(), IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
-                              pulse_animation_complete)),
+            controller(),
+            IgnoreArgs<ProgressRingAnimation*>(pulse_animation_complete)),
         registry()->AddProgressRingAnimationChangedCallbackForKey(
-            item_1, IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
-                        pulse_animation_complete)),
+            item_1,
+            IgnoreArgs<ProgressRingAnimation*>(pulse_animation_complete)),
         registry()->AddProgressRingAnimationChangedCallbackForKey(
-            item_2, IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
-                        pulse_animation_complete))};
+            item_2,
+            IgnoreArgs<ProgressRingAnimation*>(pulse_animation_complete))};
 
     run_loop.Run();
   }
diff --git a/ash/system/holding_space/holding_space_item_chip_view.cc b/ash/system/holding_space/holding_space_item_chip_view.cc
index 055b9fc2..69afbb1 100644
--- a/ash/system/holding_space/holding_space_item_chip_view.cc
+++ b/ash/system/holding_space/holding_space_item_chip_view.cc
@@ -19,8 +19,8 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "ash/system/holding_space/holding_space_progress_indicator.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
 #include "ash/system/holding_space/holding_space_view_delegate.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -336,10 +336,9 @@
   progress_ring_animation_changed_subscription_ =
       HoldingSpaceAnimationRegistry::GetInstance()
           ->AddProgressRingAnimationChangedCallbackForKey(
-              item, IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
-                        base::BindRepeating(
-                            &HoldingSpaceItemChipView::UpdateImageTransform,
-                            base::Unretained(this))));
+              item, IgnoreArgs<ProgressRingAnimation*>(base::BindRepeating(
+                        &HoldingSpaceItemChipView::UpdateImageTransform,
+                        base::Unretained(this))));
 
   UpdateImage();
   UpdateImageAndProgressIndicatorVisibility();
@@ -547,7 +546,7 @@
   const bool is_item_visibly_in_progress =
       !item()->progress().IsHidden() && !item()->progress().IsComplete();
 
-  const HoldingSpaceProgressRingAnimation* progress_ring_animation =
+  const ProgressRingAnimation* progress_ring_animation =
       HoldingSpaceAnimationRegistry::GetInstance()
           ->GetProgressRingAnimationForKey(item());
 
diff --git a/ash/system/holding_space/holding_space_progress_icon_animation.h b/ash/system/holding_space/holding_space_progress_icon_animation.h
deleted file mode 100644
index 20dd4a9..0000000
--- a/ash/system/holding_space/holding_space_progress_icon_animation.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2022 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.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
-
-#include "ash/ash_export.h"
-#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
-
-namespace ash {
-
-// An animation for a `HoldingSpaceProgressIndicator`'s icon.
-class ASH_EXPORT HoldingSpaceProgressIconAnimation
-    : public HoldingSpaceProgressIndicatorAnimation {
- public:
-  HoldingSpaceProgressIconAnimation();
-  HoldingSpaceProgressIconAnimation(const HoldingSpaceProgressIconAnimation&) =
-      delete;
-  HoldingSpaceProgressIconAnimation& operator=(
-      const HoldingSpaceProgressIconAnimation&) = delete;
-  ~HoldingSpaceProgressIconAnimation() override;
-
-  // Animatable properties.
-  float inner_icon_translate_y_scale_factor() const {
-    return inner_icon_translate_y_scale_factor_;
-  }
-  float inner_ring_stroke_width_scale_factor() const {
-    return inner_ring_stroke_width_scale_factor_;
-  }
-
- private:
-  // HoldingSpaceProgressIndicatorAnimation:
-  void UpdateAnimatableProperties(double fraction) override;
-
-  // Animatable properties.
-  float inner_icon_translate_y_scale_factor_ = 0.f;
-  float inner_ring_stroke_width_scale_factor_ = 1.f;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_indicator.cc b/ash/system/holding_space/holding_space_progress_indicator.cc
index d4a6d7e..0dea86e 100644
--- a/ash/system/holding_space/holding_space_progress_indicator.cc
+++ b/ash/system/holding_space/holding_space_progress_indicator.cc
@@ -14,9 +14,9 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/holding_space/holding_space_animation_registry.h"
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
 #include "ash/system/progress_indicator/progress_indicator_animation_registry.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "base/scoped_observation.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkPathBuilder.h"
@@ -146,6 +146,168 @@
              : cc::PaintFlags::Cap::kRound_Cap;
 }
 
+// DefaultProgressIndicatorAnimationRegistry -----------------------------------
+
+// A default implementation of `ProgressIndicatorAnimationRegistry` which is
+// associated with a single `HoldingSpaceProgressIndicator` and manage progress
+// animations as needed.
+class DefaultProgressIndicatorAnimationRegistry
+    : public ProgressIndicatorAnimationRegistry {
+ public:
+  DefaultProgressIndicatorAnimationRegistry() = default;
+  DefaultProgressIndicatorAnimationRegistry(
+      const DefaultProgressIndicatorAnimationRegistry&) = delete;
+  DefaultProgressIndicatorAnimationRegistry& operator=(
+      const DefaultProgressIndicatorAnimationRegistry&) = delete;
+  ~DefaultProgressIndicatorAnimationRegistry() = default;
+
+  // Sets the `progress_indicator` for which this registry manages animations.
+  // NOTE: This method may be called only once.
+  void SetProgressIndicator(HoldingSpaceProgressIndicator* progress_indicator) {
+    DCHECK(progress_indicator);
+    DCHECK(!progress_indicator_);
+    progress_indicator_ = progress_indicator;
+    progress_changed_subscription_ =
+        progress_indicator_->AddProgressChangedCallback(base::BindRepeating(
+            &DefaultProgressIndicatorAnimationRegistry::OnProgressChanged,
+            weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  // Invoked on changes to `progress_indicator_` progress.
+  void OnProgressChanged() {
+    const absl::optional<float>& progress = progress_indicator_->progress();
+    if (!progress.has_value()) {
+      // Progress is indeterminate.
+      EnsureProgressIconAnimation();
+      EnsureProgressRingAnimationOfType(
+          ProgressRingAnimation::Type::kIndeterminate);
+    } else if (progress != HoldingSpaceProgressIndicator::kProgressComplete) {
+      // Progress is determinate.
+      EnsureProgressIconAnimation();
+      EraseProgressRingAnimation();
+    } else if (previous_progress_ !=
+               HoldingSpaceProgressIndicator::kProgressComplete) {
+      // Progress is complete.
+      EraseProgressIconAnimation();
+      EnsureProgressRingAnimationOfType(ProgressRingAnimation::Type::kPulse);
+    }
+    previous_progress_ = progress;
+  }
+
+  // Invoked on update of the specified `animation`.
+  void OnProgressRingAnimationUpdated(ProgressRingAnimation* animation) {
+    if (animation->IsAnimating())
+      return;
+
+    // On completion, `animation` can be removed from the registry. This cannot
+    // be done directly from `animation`'s subscription callback, so post a task
+    // to delete `animation` as soon as possible.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](const base::WeakPtr<DefaultProgressIndicatorAnimationRegistry>&
+                   registry,
+               ProgressRingAnimation* animation) {
+              if (!registry)
+                return;
+              auto* key = registry->progress_indicator_;
+              if (registry->GetProgressRingAnimationForKey(key) == animation)
+                registry->SetProgressRingAnimationForKey(key, nullptr);
+            },
+            weak_ptr_factory_.GetWeakPtr(), animation));
+  }
+
+  // Ensures that a progress icon animation exists and is started. NOTE: This
+  // method no-ops if in-progress animation v2 is disabled since in such cases
+  // there is no progress icon to animate.
+  void EnsureProgressIconAnimation() {
+    if (!features::IsHoldingSpaceInProgressAnimationV2Enabled())
+      return;
+
+    if (!GetProgressIconAnimationForKey(progress_indicator_)) {
+      SetProgressIconAnimationForKey(progress_indicator_,
+                                     std::make_unique<ProgressIconAnimation>())
+          ->Start();
+    }
+  }
+
+  // Ensures that a progress ring animation of the specified `type` exists and
+  // is started.
+  void EnsureProgressRingAnimationOfType(ProgressRingAnimation::Type type) {
+    auto* ring_animation = GetProgressRingAnimationForKey(progress_indicator_);
+    if (ring_animation && ring_animation->type() == type)
+      return;
+
+    auto animation = ProgressRingAnimation::CreateOfType(type);
+
+    // NOTE: `animation` is owned by `this` so it is safe to use a raw pointer
+    // and subscription-less callback.
+    animation->AddUnsafeAnimationUpdatedCallback(
+        base::BindRepeating(&DefaultProgressIndicatorAnimationRegistry::
+                                OnProgressRingAnimationUpdated,
+                            base::Unretained(this), animation.get()));
+
+    SetProgressRingAnimationForKey(progress_indicator_, std::move(animation))
+        ->Start();
+  }
+
+  // Erases any existing progress icon animation.
+  void EraseProgressIconAnimation() {
+    SetProgressIconAnimationForKey(progress_indicator_, nullptr);
+  }
+
+  // Erases any existing progress ring animation.
+  void EraseProgressRingAnimation() {
+    SetProgressRingAnimationForKey(progress_indicator_, nullptr);
+  }
+
+  // The progress indicator for which to manage animations and a subscription
+  // to receive notification of progress change events.
+  HoldingSpaceProgressIndicator* progress_indicator_ = nullptr;
+  base::CallbackListSubscription progress_changed_subscription_;
+
+  // Instantiate `previous_progress_` to completion to avoid starting a pulse
+  // animation on first progress update.
+  absl::optional<float> previous_progress_ =
+      HoldingSpaceProgressIndicator::kProgressComplete;
+
+  base::WeakPtrFactory<DefaultProgressIndicatorAnimationRegistry>
+      weak_ptr_factory_{this};
+};
+
+// DefaultProgressIndicator ----------------------------------------------------
+
+// A default implementation of `HoldingSpaceProgressIndicator` which paints
+// indication of progress returned by the specified `progress_callback_`.
+// NOTE: This instance comes pre-wired with an animation `registry_` that will
+// manage progress animations as needed.
+class DefaultProgressIndicator : public HoldingSpaceProgressIndicator {
+ public:
+  DefaultProgressIndicator(
+      std::unique_ptr<DefaultProgressIndicatorAnimationRegistry> registry,
+      base::RepeatingCallback<absl::optional<float>()> progress_callback)
+      : HoldingSpaceProgressIndicator(/*registry=*/registry.get(),
+                                      /*animation_key=*/this),
+        registry_(std::move(registry)),
+        progress_callback_(std::move(progress_callback)) {
+    registry_->SetProgressIndicator(this);
+  }
+
+  DefaultProgressIndicator(const DefaultProgressIndicator&) = delete;
+  DefaultProgressIndicator& operator=(const DefaultProgressIndicator&) = delete;
+  ~DefaultProgressIndicator() override = default;
+
+ private:
+  // HoldingSpaceProgressIndicator:
+  absl::optional<float> CalculateProgress() const override {
+    return progress_callback_.Run();
+  }
+
+  std::unique_ptr<DefaultProgressIndicatorAnimationRegistry> registry_;
+  base::RepeatingCallback<absl::optional<float>()> progress_callback_;
+};
+
 // HoldingSpaceControllerProgressIndicator -------------------------------------
 
 // A class owning a `ui::Layer` which paints indication of progress for all
@@ -325,7 +487,7 @@
 
   // If an `icon_animation` is already registered, perform additional
   // initialization.
-  HoldingSpaceProgressIconAnimation* icon_animation =
+  ProgressIconAnimation* icon_animation =
       animation_registry_->GetProgressIconAnimationForKey(animation_key_);
   if (icon_animation)
     OnProgressIconAnimationChanged(icon_animation);
@@ -342,7 +504,7 @@
 
   // If `ring_animation` is already registered, perform additional
   // initialization.
-  HoldingSpaceProgressRingAnimation* ring_animation =
+  ProgressRingAnimation* ring_animation =
       animation_registry_->GetProgressRingAnimationForKey(animation_key_);
   if (ring_animation)
     OnProgressRingAnimationChanged(ring_animation);
@@ -352,6 +514,15 @@
 
 // static
 std::unique_ptr<HoldingSpaceProgressIndicator>
+HoldingSpaceProgressIndicator::CreateDefaultInstance(
+    base::RepeatingCallback<absl::optional<float>()> progress_callback) {
+  return std::make_unique<DefaultProgressIndicator>(
+      std::make_unique<DefaultProgressIndicatorAnimationRegistry>(),
+      std::move(progress_callback));
+}
+
+// static
+std::unique_ptr<HoldingSpaceProgressIndicator>
 HoldingSpaceProgressIndicator::CreateForController(
     HoldingSpaceController* controller) {
   return std::make_unique<HoldingSpaceControllerProgressIndicator>(controller);
@@ -412,7 +583,7 @@
 void HoldingSpaceProgressIndicator::OnPaintLayer(
     const ui::PaintContext& context) {
   // Look up the associated `ring_animation` (if one exists).
-  HoldingSpaceProgressRingAnimation* ring_animation =
+  ProgressRingAnimation* ring_animation =
       animation_registry_
           ? animation_registry_->GetProgressRingAnimationForKey(animation_key_)
           : nullptr;
@@ -492,7 +663,7 @@
     return;
 
   // Look up the associated `icon_animation` (if one exists).
-  HoldingSpaceProgressIconAnimation* icon_animation =
+  ProgressIconAnimation* icon_animation =
       animation_registry_
           ? animation_registry_->GetProgressIconAnimationForKey(animation_key_)
           : nullptr;
@@ -551,7 +722,7 @@
 }
 
 void HoldingSpaceProgressIndicator::OnProgressIconAnimationChanged(
-    HoldingSpaceProgressIconAnimation* animation) {
+    ProgressIconAnimation* animation) {
   // Trigger repaint of this progress indicator on `animation` updates. Note
   // that it is safe to use a raw pointer here since `this` owns the
   // subscription.
@@ -565,7 +736,7 @@
 }
 
 void HoldingSpaceProgressIndicator::OnProgressRingAnimationChanged(
-    HoldingSpaceProgressRingAnimation* animation) {
+    ProgressRingAnimation* animation) {
   // Trigger repaint of this progress indicator on `animation` updates. Note
   // that it is safe to use a raw pointer here since `this` owns the
   // subscription.
diff --git a/ash/system/holding_space/holding_space_progress_indicator.h b/ash/system/holding_space/holding_space_progress_indicator.h
index 78a130c..85856bc 100644
--- a/ash/system/holding_space/holding_space_progress_indicator.h
+++ b/ash/system/holding_space/holding_space_progress_indicator.h
@@ -18,9 +18,9 @@
 
 class HoldingSpaceController;
 class HoldingSpaceItem;
-class HoldingSpaceProgressIconAnimation;
-class HoldingSpaceProgressRingAnimation;
+class ProgressIconAnimation;
 class ProgressIndicatorAnimationRegistry;
+class ProgressRingAnimation;
 
 // A class owning a `ui::Layer` which paints indication of progress.
 // NOTE: The owned `layer()` is not painted if progress == `1.f`.
@@ -35,6 +35,12 @@
       const HoldingSpaceProgressIndicator&) = delete;
   ~HoldingSpaceProgressIndicator() override;
 
+  // Returns an instance which paints indication of progress returned by the
+  // specified `progress_callback`. NOTE: This instance comes pre-wired with an
+  // `animation_registry_` that will manage progress animations as needed.
+  static std::unique_ptr<HoldingSpaceProgressIndicator> CreateDefaultInstance(
+      base::RepeatingCallback<absl::optional<float>()> progress_callback);
+
   // Returns an instance which paints indication of progress for all holding
   // space items in the model attached to the specified `controller`.
   static std::unique_ptr<HoldingSpaceProgressIndicator> CreateForController(
@@ -68,6 +74,12 @@
   void SetInnerIconVisible(bool visible);
   bool inner_icon_visible() const { return inner_icon_visible_; }
 
+  // Returns the underlying `animation_registry_` in which to look up animations
+  // for the associated `animation_key_`. NOTE: This may return `nullptr`.
+  ProgressIndicatorAnimationRegistry* animation_registry() {
+    return animation_registry_;
+  }
+
   // Returns the underlying `progress_` for which to paint indication.
   // NOTE: If absent, progress is indeterminate.
   // NOTE: If present, progress must be >= `0.f` and <= `1.f`.
@@ -99,14 +111,12 @@
   // Invoked when the icon `animation` associated with this progress indicator's
   // `animation_key_` has changed in the `animation_registry_`.
   // NOTE: The specified `animation` may be `nullptr`.
-  void OnProgressIconAnimationChanged(
-      HoldingSpaceProgressIconAnimation* animation);
+  void OnProgressIconAnimationChanged(ProgressIconAnimation* animation);
 
   // Invoked when the ring `animation` associated with this progress indicator's
   // `animation_key_` has changed in the `animation_registry_`.
   // NOTE: The specified `animation` may be `nullptr`.
-  void OnProgressRingAnimationChanged(
-      HoldingSpaceProgressRingAnimation* animation);
+  void OnProgressRingAnimationChanged(ProgressRingAnimation* animation);
 
   // The animation registry in which to look up animations for the associated
   // `animation_key_`. When an animation exists, it will be painted in lieu of
diff --git a/ash/system/holding_space/holding_space_progress_indicator_unittest.cc b/ash/system/holding_space/holding_space_progress_indicator_unittest.cc
index 57951e64..30414dc 100644
--- a/ash/system/holding_space/holding_space_progress_indicator_unittest.cc
+++ b/ash/system/holding_space/holding_space_progress_indicator_unittest.cc
@@ -4,8 +4,10 @@
 
 #include "ash/system/holding_space/holding_space_progress_indicator.h"
 
+#include "ash/system/progress_indicator/progress_indicator_animation_registry.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/bind.h"
+#include "base/test/repeating_test_future.h"
 
 namespace ash {
 namespace {
@@ -35,6 +37,79 @@
 
 using HoldingSpaceProgressIndicatorTest = AshTestBase;
 
+// Verifies that `HoldingSpaceProgressIndicator::CreateDefaultInstance()` works
+// as intended. It should delegate progress calculation to a constructor
+// provided callback and manage progress animations as needed.
+TEST_F(HoldingSpaceProgressIndicatorTest, CreateDefaultInstance) {
+  absl::optional<float> progress;
+
+  // Create a default instance of `HoldingSpaceProgressIndicator` that paints
+  // `progress` whenever visual state is updated.
+  auto progress_indicator =
+      HoldingSpaceProgressIndicator::CreateDefaultInstance(
+          base::BindLambdaForTesting([&]() { return progress; }));
+
+  // Cache `layer_delegate` associate with `progress_indicator` to manually
+  // trigger update of visual state.
+  auto* layer_delegate =
+      static_cast<ui::LayerDelegate*>(progress_indicator.get());
+
+  // Cache animation `key` and `registry` associated with `progress_indicator`.
+  auto* key = progress_indicator.get();
+  auto* registry = progress_indicator->animation_registry();
+
+  // Verify initial progress and animation states.
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_FALSE(registry->GetProgressIconAnimationForKey(key));
+  EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key));
+
+  // Update `progress` to 0%. Verify progress and animation states.
+  progress = 0.f;
+  layer_delegate->UpdateVisualState();
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key));
+  EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key));
+
+  // Update `progress` to 50%. Verify progress and animation states.
+  progress = 0.5f;
+  layer_delegate->UpdateVisualState();
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key));
+  EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key));
+
+  // Update `progress` to indeterminate. Verify progress and animation states.
+  progress = absl::nullopt;
+  layer_delegate->UpdateVisualState();
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key));
+  ASSERT_TRUE(registry->GetProgressRingAnimationForKey(key));
+  EXPECT_EQ(registry->GetProgressRingAnimationForKey(key)->type(),
+            ProgressRingAnimation::Type::kIndeterminate);
+
+  // Update `progress` to 75%. Verify progress and animation states.
+  progress = 0.75f;
+  layer_delegate->UpdateVisualState();
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key));
+  EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key));
+
+  // Update `progress` to 100%. Verify progress an animation states.
+  progress = HoldingSpaceProgressIndicator::kProgressComplete;
+  layer_delegate->UpdateVisualState();
+  EXPECT_EQ(progress_indicator->progress(), progress);
+  EXPECT_FALSE(registry->GetProgressIconAnimationForKey(key));
+  ASSERT_TRUE(registry->GetProgressRingAnimationForKey(key));
+  EXPECT_EQ(registry->GetProgressRingAnimationForKey(key)->type(),
+            ProgressRingAnimation::Type::kPulse);
+
+  // The pulse animation that runs on progress completion should be removed
+  // automatically on animation completion.
+  base::test::RepeatingTestFuture<ProgressRingAnimation*> future;
+  auto subscription = registry->AddProgressRingAnimationChangedCallbackForKey(
+      key, future.GetCallback());
+  EXPECT_EQ(future.Take(), nullptr);
+}
+
 // Verifies that `HoldingSpaceProgressIndicator::AddProgressChangedCallback()`
 // works as intended.
 TEST_F(HoldingSpaceProgressIndicatorTest, AddProgressChangedCallback) {
diff --git a/ash/system/holding_space/holding_space_progress_ring_animation.cc b/ash/system/holding_space/holding_space_progress_ring_animation.cc
deleted file mode 100644
index a5a0dcd4..0000000
--- a/ash/system/holding_space/holding_space_progress_ring_animation.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 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.
-
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
-
-#include "ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_pulse_animation.h"
-
-namespace ash {
-
-HoldingSpaceProgressRingAnimation::HoldingSpaceProgressRingAnimation(
-    Type type,
-    base::TimeDelta duration,
-    bool is_cyclic)
-    : HoldingSpaceProgressIndicatorAnimation(duration, is_cyclic),
-      type_(type) {}
-
-HoldingSpaceProgressRingAnimation::~HoldingSpaceProgressRingAnimation() =
-    default;
-
-// static
-std::unique_ptr<HoldingSpaceProgressRingAnimation>
-HoldingSpaceProgressRingAnimation::CreateOfType(Type type) {
-  switch (type) {
-    case Type::kIndeterminate:
-      return std::make_unique<HoldingSpaceProgressRingIndeterminateAnimation>();
-    case Type::kPulse:
-      return std::make_unique<HoldingSpaceProgressRingPulseAnimation>();
-  }
-}
-
-void HoldingSpaceProgressRingAnimation::UpdateAnimatableProperties(
-    double fraction) {
-  UpdateAnimatableProperties(fraction, &start_position_, &end_position_,
-                             &opacity_);
-}
-
-}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_progress_ring_animation.h b/ash/system/holding_space/holding_space_progress_ring_animation.h
deleted file mode 100644
index 3e38e90..0000000
--- a/ash/system/holding_space/holding_space_progress_ring_animation.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_ANIMATION_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_ANIMATION_H_
-
-#include <memory>
-
-#include "ash/ash_export.h"
-#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
-
-namespace ash {
-
-// An animation for a `HoldingSpaceProgressIndicator` to be painted in lieu of
-// the determinate progress ring that would otherwise be painted.
-class ASH_EXPORT HoldingSpaceProgressRingAnimation
-    : public HoldingSpaceProgressIndicatorAnimation {
- public:
-  enum class Type {
-    kIndeterminate,  // See `HoldingSpaceProgressRingIndeterminateAnimation`.
-    kPulse,          // See `HoldingSpaceProgressRingPulseAnimation`.
-  };
-
-  HoldingSpaceProgressRingAnimation(const HoldingSpaceProgressRingAnimation&) =
-      delete;
-  HoldingSpaceProgressRingAnimation& operator=(
-      const HoldingSpaceProgressRingAnimation&) = delete;
-  ~HoldingSpaceProgressRingAnimation() override;
-
-  // Returns a created progress ring animation of the specified `type`.
-  static std::unique_ptr<HoldingSpaceProgressRingAnimation> CreateOfType(
-      Type type);
-
-  // Returns the specific type of this animation.
-  Type type() const { return type_; }
-
-  // Returns animatable properties.
-  float start_position() const { return start_position_; }
-  float end_position() const { return end_position_; }
-  float opacity() const { return opacity_; }
-
- protected:
-  HoldingSpaceProgressRingAnimation(Type type,
-                                    base::TimeDelta duration,
-                                    bool is_cyclic);
-
-  // Implementing classes should update any desired animatable properties as
-  // appropriate for the specified animation `fraction`.
-  virtual void UpdateAnimatableProperties(double fraction,
-                                          float* start_position,
-                                          float* end_position,
-                                          float* opacity) = 0;
-
- private:
-  // HoldingSpaceProgressIndicatorAnimation:
-  void UpdateAnimatableProperties(double fraction) override;
-
-  // The specific type of this animation.
-  const Type type_;
-
-  // Animatable properties.
-  float start_position_ = 0.f;
-  float end_position_ = 1.f;
-  float opacity_ = 1.f;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h b/ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h
deleted file mode 100644
index 064e5b5..0000000
--- a/ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
-
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
-
-namespace ash {
-
-// An animation for a `HoldingSpaceProgressRing` to paint an indeterminate
-// progress ring in lieu of the determinate progress ring that would otherwise
-// be painted.
-class HoldingSpaceProgressRingIndeterminateAnimation
-    : public HoldingSpaceProgressRingAnimation {
- public:
-  HoldingSpaceProgressRingIndeterminateAnimation();
-  HoldingSpaceProgressRingIndeterminateAnimation(
-      const HoldingSpaceProgressRingIndeterminateAnimation&) = delete;
-  HoldingSpaceProgressRingIndeterminateAnimation& operator=(
-      const HoldingSpaceProgressRingIndeterminateAnimation&) = delete;
-  ~HoldingSpaceProgressRingIndeterminateAnimation() override;
-
- private:
-  // HoldingSpaceProgressRingAnimation:
-  void UpdateAnimatableProperties(double fraction,
-                                  float* start_value,
-                                  float* end_value,
-                                  float* opacity) override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_ring_pulse_animation.h b/ash/system/holding_space/holding_space_progress_ring_pulse_animation.h
deleted file mode 100644
index bf3b3cc..0000000
--- a/ash/system/holding_space/holding_space_progress_ring_pulse_animation.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_PULSE_ANIMATION_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_PULSE_ANIMATION_H_
-
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
-
-namespace ash {
-
-// An animation for a `HoldingSpaceProgressRing` to paint a pulsing progress
-// ring in lieu of the determinate progress ring that would otherwise be
-// painted.
-class HoldingSpaceProgressRingPulseAnimation
-    : public HoldingSpaceProgressRingAnimation {
- public:
-  HoldingSpaceProgressRingPulseAnimation();
-  HoldingSpaceProgressRingPulseAnimation(
-      const HoldingSpaceProgressRingPulseAnimation&) = delete;
-  HoldingSpaceProgressRingPulseAnimation& operator=(
-      const HoldingSpaceProgressRingPulseAnimation&) = delete;
-  ~HoldingSpaceProgressRingPulseAnimation() override;
-
- private:
-  // HoldingSpaceProgressRingAnimation:
-  void UpdateAnimatableProperties(double fraction,
-                                  float* start_value,
-                                  float* end_value,
-                                  float* opacity) override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_RING_PULSE_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 6c02bcf..2ba04cb7 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -535,14 +535,16 @@
         static_cast<int>(HoldingSpaceCommandId::kHidePreviews),
         l10n_util::GetStringUTF16(
             IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_HIDE_PREVIEWS),
-        ui::ImageModel::FromVectorIcon(kVisibilityOffIcon, ui::kColorMenuIcon,
+        ui::ImageModel::FromVectorIcon(kVisibilityOffIcon,
+                                       ui::kColorAshSystemUIMenuIcon,
                                        kHoldingSpaceIconSize));
   } else {
     context_menu_model->AddItemWithIcon(
         static_cast<int>(HoldingSpaceCommandId::kShowPreviews),
         l10n_util::GetStringUTF16(
             IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_SHOW_PREVIEWS),
-        ui::ImageModel::FromVectorIcon(kVisibilityIcon, ui::kColorMenuIcon,
+        ui::ImageModel::FromVectorIcon(kVisibilityIcon,
+                                       ui::kColorAshSystemUIMenuIcon,
                                        kHoldingSpaceIconSize));
   }
 
diff --git a/ash/system/holding_space/holding_space_tray_icon_preview.cc b/ash/system/holding_space/holding_space_tray_icon_preview.cc
index 11e24bd..4efc6d84 100644
--- a/ash/system/holding_space/holding_space_tray_icon_preview.cc
+++ b/ash/system/holding_space/holding_space_tray_icon_preview.cc
@@ -157,7 +157,7 @@
         HoldingSpaceAnimationRegistry::GetInstance()
             ->AddProgressRingAnimationChangedCallbackForKey(
                 /*animation_key=*/item_,
-                IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
+                IgnoreArgs<ProgressRingAnimation*>(
                     base::BindRepeating(&ImageLayerOwner::UpdateTransform,
                                         base::Unretained(this))));
 
diff --git a/ash/system/holding_space/holding_space_view_delegate.cc b/ash/system/holding_space/holding_space_view_delegate.cc
index f7e8f77..0d14631 100644
--- a/ash/system/holding_space/holding_space_view_delegate.cc
+++ b/ash/system/holding_space/holding_space_view_delegate.cc
@@ -700,7 +700,8 @@
       context_menu_model_->AddItemWithIcon(
           static_cast<int>(menu_item.command_id),
           l10n_util::GetStringUTF16(menu_item.label_id),
-          ui::ImageModel::FromVectorIcon(menu_item.icon, ui::kColorMenuIcon,
+          ui::ImageModel::FromVectorIcon(menu_item.icon,
+                                         ui::kColorAshSystemUIMenuIcon,
                                          kHoldingSpaceIconSize));
     }
   }
diff --git a/ash/system/network/active_network_icon_unittest.cc b/ash/system/network/active_network_icon_unittest.cc
index 9656722..27f84e91 100644
--- a/ash/system/network/active_network_icon_unittest.cc
+++ b/ash/system/network/active_network_icon_unittest.cc
@@ -21,8 +21,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
-// Flaky on Chrome OS debug. http://crbug.com/1293903
-#if BUILDFLAG(IS_CHROMEOS) && !defined(NDEBUG)
+// Flaky on Chrome OS debug and Linux Chromium OS ASan LSan Tests. b/217762791
+#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
 #define MAYBE_GetSingleImage DISABLED_GetSingleImage
 #define MAYBE_CellularUninitialized DISABLED_CellularUninitialized
 #define MAYBE_CellularScanning DISABLED_CellularScanning
diff --git a/ash/system/network/network_feature_pod_controller_unittest.cc b/ash/system/network/network_feature_pod_controller_unittest.cc
index cf84558..3b7f3778 100644
--- a/ash/system/network/network_feature_pod_controller_unittest.cc
+++ b/ash/system/network/network_feature_pod_controller_unittest.cc
@@ -35,8 +35,8 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button.h"
 
-// Flaky on Chrome OS debug. http://crbug.com/1293903
-#if BUILDFLAG(IS_CHROMEOS) && !defined(NDEBUG)
+// Flaky on Chrome OS debug and Linux Chromium OS ASan LSan Tests. b/217762791
+#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
 #define MAYBE_HasCorrectButtonStateWhenNetworkStateChanges \
   DISABLED_HasCorrectButtonStateWhenNetworkStateChanges
 #define MAYBE_PressingIconOrLabelIsHandledCorrectly_Cellular \
diff --git a/ash/system/network/network_icon_unittest.cc b/ash/system/network/network_icon_unittest.cc
index f4263a7..f57b7d0 100644
--- a/ash/system/network/network_icon_unittest.cc
+++ b/ash/system/network/network_icon_unittest.cc
@@ -22,8 +22,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
-// Flaky on Chrome OS debug. http://crbug.com/1293903
-#if BUILDFLAG(IS_CHROMEOS) && !defined(NDEBUG)
+// Flaky on Chrome OS debug and Linux Chromium OS ASan LSan Tests. b/217762791
+#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
 #define MAYBE_DefaultImageWifiConnecting DISABLED_DefaultImageWifiConnecting
 #define MAYBE_DefaultImageReconnectingWifiWithCellularConnected \
   DISABLED_DefaultImageReconnectingWifiWithCellularConnected
diff --git a/ash/system/phonehub/camera_roll_menu_model.cc b/ash/system/phonehub/camera_roll_menu_model.cc
index c1086e7..ba571c17 100644
--- a/ash/system/phonehub/camera_roll_menu_model.cc
+++ b/ash/system/phonehub/camera_roll_menu_model.cc
@@ -25,8 +25,8 @@
                   l10n_util::GetStringUTF16(
                       IDS_ASH_PHONE_HUB_CAMERA_ROLL_MENU_DOWNLOAD_LABEL),
                   ui::ImageModel::FromVectorIcon(
-                      kPhoneHubCameraRollMenuDownloadIcon, ui::kColorMenuIcon,
-                      kCameraRollMenuIconSize));
+                      kPhoneHubCameraRollMenuDownloadIcon,
+                      ui::kColorAshSystemUIMenuIcon, kCameraRollMenuIconSize));
 }
 
 CameraRollMenuModel::~CameraRollMenuModel() {}
diff --git a/ash/system/holding_space/holding_space_progress_icon_animation.cc b/ash/system/progress_indicator/progress_icon_animation.cc
similarity index 66%
rename from ash/system/holding_space/holding_space_progress_icon_animation.cc
rename to ash/system/progress_indicator/progress_icon_animation.cc
index eb052694..be0a80b 100644
--- a/ash/system/holding_space/holding_space_progress_icon_animation.cc
+++ b/ash/system/progress_indicator/progress_icon_animation.cc
@@ -2,22 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
 
 #include "ui/gfx/animation/tween.h"
 
 namespace ash {
 
-HoldingSpaceProgressIconAnimation::HoldingSpaceProgressIconAnimation()
-    : HoldingSpaceProgressIndicatorAnimation(
+ProgressIconAnimation::ProgressIconAnimation()
+    : ProgressIndicatorAnimation(
           /*duration=*/base::Milliseconds(400),
           /*is_cyclic=*/false) {}
 
-HoldingSpaceProgressIconAnimation::~HoldingSpaceProgressIconAnimation() =
-    default;
+ProgressIconAnimation::~ProgressIconAnimation() = default;
 
-void HoldingSpaceProgressIconAnimation::UpdateAnimatableProperties(
-    double fraction) {
+void ProgressIconAnimation::UpdateAnimatableProperties(double fraction) {
   // Tween.
   fraction = gfx::Tween::CalculateValue(gfx::Tween::Type::ACCEL_20_DECEL_100,
                                         fraction);
diff --git a/ash/system/progress_indicator/progress_icon_animation.h b/ash/system/progress_indicator/progress_icon_animation.h
new file mode 100644
index 0000000..b164f10
--- /dev/null
+++ b/ash/system/progress_indicator/progress_icon_animation.h
@@ -0,0 +1,40 @@
+// Copyright 2022 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.
+
+#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_ICON_ANIMATION_H_
+#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_ICON_ANIMATION_H_
+
+#include "ash/ash_export.h"
+#include "ash/system/progress_indicator/progress_indicator_animation.h"
+
+namespace ash {
+
+// An animation for a `HoldingSpaceProgressIndicator`'s icon.
+class ASH_EXPORT ProgressIconAnimation : public ProgressIndicatorAnimation {
+ public:
+  ProgressIconAnimation();
+  ProgressIconAnimation(const ProgressIconAnimation&) = delete;
+  ProgressIconAnimation& operator=(const ProgressIconAnimation&) = delete;
+  ~ProgressIconAnimation() override;
+
+  // Animatable properties.
+  float inner_icon_translate_y_scale_factor() const {
+    return inner_icon_translate_y_scale_factor_;
+  }
+  float inner_ring_stroke_width_scale_factor() const {
+    return inner_ring_stroke_width_scale_factor_;
+  }
+
+ private:
+  // ProgressIndicatorAnimation:
+  void UpdateAnimatableProperties(double fraction) override;
+
+  // Animatable properties.
+  float inner_icon_translate_y_scale_factor_ = 0.f;
+  float inner_ring_stroke_width_scale_factor_ = 1.f;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_ICON_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_indicator_animation.cc b/ash/system/progress_indicator/progress_indicator_animation.cc
similarity index 68%
rename from ash/system/holding_space/holding_space_progress_indicator_animation.cc
rename to ash/system/progress_indicator/progress_indicator_animation.cc
index 7d50d79..6211524 100644
--- a/ash/system/holding_space/holding_space_progress_indicator_animation.cc
+++ b/ash/system/progress_indicator/progress_indicator_animation.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
+#include "ash/system/progress_indicator/progress_indicator_animation.h"
 
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -10,40 +10,38 @@
 
 namespace ash {
 
-HoldingSpaceProgressIndicatorAnimation::HoldingSpaceProgressIndicatorAnimation(
-    base::TimeDelta duration,
-    bool is_cyclic)
+ProgressIndicatorAnimation::ProgressIndicatorAnimation(base::TimeDelta duration,
+                                                       bool is_cyclic)
     : duration_(duration), is_cyclic_(is_cyclic) {}
 
-HoldingSpaceProgressIndicatorAnimation::
-    ~HoldingSpaceProgressIndicatorAnimation() = default;
+ProgressIndicatorAnimation::~ProgressIndicatorAnimation() = default;
 
 base::CallbackListSubscription
-HoldingSpaceProgressIndicatorAnimation::AddAnimationUpdatedCallback(
+ProgressIndicatorAnimation::AddAnimationUpdatedCallback(
     base::RepeatingClosureList::CallbackType callback) {
   return animation_updated_callback_list_.Add(std::move(callback));
 }
 
-void HoldingSpaceProgressIndicatorAnimation::AddUnsafeAnimationUpdatedCallback(
+void ProgressIndicatorAnimation::AddUnsafeAnimationUpdatedCallback(
     base::RepeatingClosureList::CallbackType callback) {
   animation_updated_callback_list_.AddUnsafe(std::move(callback));
 }
 
-void HoldingSpaceProgressIndicatorAnimation::Start() {
+void ProgressIndicatorAnimation::Start() {
   StartInternal(/*is_cyclic_restart=*/false);
 }
 
-bool HoldingSpaceProgressIndicatorAnimation::IsAnimating() const {
+bool ProgressIndicatorAnimation::IsAnimating() const {
   return animator_ && animator_->is_animating();
 }
 
-void HoldingSpaceProgressIndicatorAnimation::AnimationProgressed(
+void ProgressIndicatorAnimation::AnimationProgressed(
     const gfx::Animation* animation) {
   UpdateAnimatableProperties(animation->GetCurrentValue());
   animation_updated_callback_list_.Notify();
 }
 
-void HoldingSpaceProgressIndicatorAnimation::AnimationEnded(
+void ProgressIndicatorAnimation::AnimationEnded(
     const gfx::Animation* animation) {
   if (!is_cyclic_) {
     animation_updated_callback_list_.Notify();
@@ -57,7 +55,7 @@
   if (ui::ScopedAnimationDurationScaleMode::duration_multiplier() == 0.f) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(&HoldingSpaceProgressIndicatorAnimation::StartInternal,
+        base::BindOnce(&ProgressIndicatorAnimation::StartInternal,
                        weak_factory_.GetWeakPtr(), /*is_cyclic_restart=*/true));
     return;
   }
@@ -65,8 +63,7 @@
   StartInternal(/*is_cyclic_restart=*/true);
 }
 
-void HoldingSpaceProgressIndicatorAnimation::StartInternal(
-    bool is_cyclic_restart) {
+void ProgressIndicatorAnimation::StartInternal(bool is_cyclic_restart) {
   animator_ = std::make_unique<gfx::SlideAnimation>(this);
   animator_->SetSlideDuration(
       ui::ScopedAnimationDurationScaleMode::duration_multiplier() * duration_);
diff --git a/ash/system/holding_space/holding_space_progress_indicator_animation.h b/ash/system/progress_indicator/progress_indicator_animation.h
similarity index 75%
rename from ash/system/holding_space/holding_space_progress_indicator_animation.h
rename to ash/system/progress_indicator/progress_indicator_animation.h
index 0d99f3b..15805da 100644
--- a/ash/system/holding_space/holding_space_progress_indicator_animation.h
+++ b/ash/system/progress_indicator/progress_indicator_animation.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_INDICATOR_ANIMATION_H_
-#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_INDICATOR_ANIMATION_H_
+#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_ANIMATION_H_
+#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_ANIMATION_H_
 
 #include <memory>
 
@@ -19,14 +19,12 @@
 namespace ash {
 
 // An animation for a `HoldingSpaceProgressIndicator`.
-class ASH_EXPORT HoldingSpaceProgressIndicatorAnimation
-    : public gfx::AnimationDelegate {
+class ASH_EXPORT ProgressIndicatorAnimation : public gfx::AnimationDelegate {
  public:
-  HoldingSpaceProgressIndicatorAnimation(
-      const HoldingSpaceProgressIndicatorAnimation&) = delete;
-  HoldingSpaceProgressIndicatorAnimation& operator=(
-      const HoldingSpaceProgressIndicatorAnimation&) = delete;
-  ~HoldingSpaceProgressIndicatorAnimation() override;
+  ProgressIndicatorAnimation(const ProgressIndicatorAnimation&) = delete;
+  ProgressIndicatorAnimation& operator=(const ProgressIndicatorAnimation&) =
+      delete;
+  ~ProgressIndicatorAnimation() override;
 
   // Adds the specified `callback` to be notified of animation updates. The
   // `callback` will continue to receive events so long as both `this` and the
@@ -52,8 +50,7 @@
   base::TimeTicks start_time() const { return start_time_; }
 
  protected:
-  HoldingSpaceProgressIndicatorAnimation(base::TimeDelta duration,
-                                         bool is_cyclic);
+  ProgressIndicatorAnimation(base::TimeDelta duration, bool is_cyclic);
 
   // Implementing classes should update any desired animatable properties as
   // appropriate for the specified animation `fraction`.
@@ -83,10 +80,9 @@
   // The list of callbacks for which to notify animation updates.
   base::RepeatingClosureList animation_updated_callback_list_;
 
-  base::WeakPtrFactory<HoldingSpaceProgressIndicatorAnimation> weak_factory_{
-      this};
+  base::WeakPtrFactory<ProgressIndicatorAnimation> weak_factory_{this};
 };
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_INDICATOR_ANIMATION_H_
+#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_ANIMATION_H_
diff --git a/ash/system/progress_indicator/progress_indicator_animation_registry.cc b/ash/system/progress_indicator/progress_indicator_animation_registry.cc
index 030e71f..d8b76b9e 100644
--- a/ash/system/progress_indicator/progress_indicator_animation_registry.cc
+++ b/ash/system/progress_indicator/progress_indicator_animation_registry.cc
@@ -136,31 +136,31 @@
       &ring_animation_changed_callback_lists_by_key_, key, std::move(callback));
 }
 
-HoldingSpaceProgressIconAnimation*
+ProgressIconAnimation*
 ProgressIndicatorAnimationRegistry::GetProgressIconAnimationForKey(
     const void* key) {
   return GetAnimationForKey(&icon_animations_by_key_, key);
 }
 
-HoldingSpaceProgressRingAnimation*
+ProgressRingAnimation*
 ProgressIndicatorAnimationRegistry::GetProgressRingAnimationForKey(
     const void* key) {
   return GetAnimationForKey(&ring_animations_by_key_, key);
 }
 
-HoldingSpaceProgressIconAnimation*
+ProgressIconAnimation*
 ProgressIndicatorAnimationRegistry::SetProgressIconAnimationForKey(
     const void* key,
-    std::unique_ptr<HoldingSpaceProgressIconAnimation> animation) {
+    std::unique_ptr<ProgressIconAnimation> animation) {
   return SetAnimationForKey(&icon_animations_by_key_,
                             &icon_animation_changed_callback_lists_by_key_, key,
                             std::move(animation));
 }
 
-HoldingSpaceProgressRingAnimation*
+ProgressRingAnimation*
 ProgressIndicatorAnimationRegistry::SetProgressRingAnimationForKey(
     const void* key,
-    std::unique_ptr<HoldingSpaceProgressRingAnimation> animation) {
+    std::unique_ptr<ProgressRingAnimation> animation) {
   return SetAnimationForKey(&ring_animations_by_key_,
                             &ring_animation_changed_callback_lists_by_key_, key,
                             std::move(animation));
diff --git a/ash/system/progress_indicator/progress_indicator_animation_registry.h b/ash/system/progress_indicator/progress_indicator_animation_registry.h
index 8accfdc..444d8af4 100644
--- a/ash/system/progress_indicator/progress_indicator_animation_registry.h
+++ b/ash/system/progress_indicator/progress_indicator_animation_registry.h
@@ -9,8 +9,8 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "base/callback_forward.h"
 #include "base/callback_list.h"
 
@@ -41,7 +41,7 @@
   ~ProgressIndicatorAnimationRegistry();
 
   using ProgressIconAnimationChangedCallbackList =
-      base::RepeatingCallbackList<void(HoldingSpaceProgressIconAnimation*)>;
+      base::RepeatingCallbackList<void(ProgressIconAnimation*)>;
 
   // Adds the specified `callback` to be notified of changes to the progress
   // icon animation associated with the specified `key`. The `callback` will
@@ -52,7 +52,7 @@
       ProgressIconAnimationChangedCallbackList::CallbackType callback);
 
   using ProgressRingAnimationChangedCallbackList =
-      base::RepeatingCallbackList<void(HoldingSpaceProgressRingAnimation*)>;
+      base::RepeatingCallbackList<void(ProgressRingAnimation*)>;
 
   // Adds the specified `callback` to be notified of changes to the progress
   // ring animation associated with the specified `key`. The `callback` will
@@ -64,25 +64,23 @@
 
   // Returns the progress icon animation registered for the specified `key`.
   // NOTE: This may return `nullptr` if no such animation is registered.
-  HoldingSpaceProgressIconAnimation* GetProgressIconAnimationForKey(
-      const void* key);
+  ProgressIconAnimation* GetProgressIconAnimationForKey(const void* key);
 
   // Returns the progress ring animation registered for the specified `key`.
   // NOTE: This may return `nullptr` if no such animation is registered.
-  HoldingSpaceProgressRingAnimation* GetProgressRingAnimationForKey(
-      const void* key);
+  ProgressRingAnimation* GetProgressRingAnimationForKey(const void* key);
 
   // Sets and returns the progress icon animation registered for the specified
   // `key`. NOTE: `animation` may be `nullptr` to unregister `key`.
-  HoldingSpaceProgressIconAnimation* SetProgressIconAnimationForKey(
+  ProgressIconAnimation* SetProgressIconAnimationForKey(
       const void* key,
-      std::unique_ptr<HoldingSpaceProgressIconAnimation> animation);
+      std::unique_ptr<ProgressIconAnimation> animation);
 
   // Sets and returns the progress ring animation registered for the specified
   // `key`. NOTE: `animation` may be `nullptr` to unregister `key`.
-  HoldingSpaceProgressRingAnimation* SetProgressRingAnimationForKey(
+  ProgressRingAnimation* SetProgressRingAnimationForKey(
       const void* key,
-      std::unique_ptr<HoldingSpaceProgressRingAnimation> animation);
+      std::unique_ptr<ProgressRingAnimation> animation);
 
   // Erases all animations for all keys.
   void EraseAllAnimations();
@@ -97,7 +95,7 @@
 
  private:
   // Mapping of keys to their associated progress icon animations.
-  std::map<const void*, std::unique_ptr<HoldingSpaceProgressIconAnimation>>
+  std::map<const void*, std::unique_ptr<ProgressIconAnimation>>
       icon_animations_by_key_;
 
   // Mapping of keys to their associated icon animation changed callback lists.
@@ -107,7 +105,7 @@
       icon_animation_changed_callback_lists_by_key_;
 
   // Mapping of keys to their associated progress ring animations.
-  std::map<const void*, std::unique_ptr<HoldingSpaceProgressRingAnimation>>
+  std::map<const void*, std::unique_ptr<ProgressRingAnimation>>
       ring_animations_by_key_;
 
   // Mapping of keys to their associated ring animation changed callback lists.
diff --git a/ash/system/progress_indicator/progress_indicator_animation_registry_unittest.cc b/ash/system/progress_indicator/progress_indicator_animation_registry_unittest.cc
index ac0dc30..31774463 100644
--- a/ash/system/progress_indicator/progress_indicator_animation_registry_unittest.cc
+++ b/ash/system/progress_indicator/progress_indicator_animation_registry_unittest.cc
@@ -7,9 +7,9 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
-#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
-#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
-#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/system/progress_indicator/progress_icon_animation.h"
+#include "ash/system/progress_indicator/progress_indicator_animation.h"
+#include "ash/system/progress_indicator/progress_ring_animation.h"
 #include "base/containers/contains.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
@@ -120,29 +120,27 @@
       // Count progress icon animation changed events for `key`.
       subscriptions.push_back(
           registry()->AddProgressIconAnimationChangedCallbackForKey(
-              key, base::BindLambdaForTesting(
-                       [&](HoldingSpaceProgressIconAnimation*) {
-                         ++icon_callback_call_count;
-                       })));
+              key, base::BindLambdaForTesting([&](ProgressIconAnimation*) {
+                ++icon_callback_call_count;
+              })));
 
       // Count progress ring animation changed events for `key`.
       subscriptions.push_back(
           registry()->AddProgressRingAnimationChangedCallbackForKey(
-              key, base::BindLambdaForTesting(
-                       [&](HoldingSpaceProgressRingAnimation*) {
-                         ++ring_callback_call_count;
-                       })));
+              key, base::BindLambdaForTesting([&](ProgressRingAnimation*) {
+                ++ring_callback_call_count;
+              })));
 
       // Create a progress icon animation for `key`.
       registry()->SetProgressIconAnimationForKey(
-          key, std::make_unique<HoldingSpaceProgressIconAnimation>());
+          key, std::make_unique<ProgressIconAnimation>());
       EXPECT_TRUE(registry()->GetProgressIconAnimationForKey(key));
       EXPECT_EQ(icon_callback_call_count, index + 1u);
 
       // Create a progress ring animation for `key`.
       registry()->SetProgressRingAnimationForKey(
-          key, HoldingSpaceProgressRingAnimation::CreateOfType(
-                   HoldingSpaceProgressRingAnimation::Type::kPulse));
+          key, ProgressRingAnimation::CreateOfType(
+                   ProgressRingAnimation::Type::kPulse));
       EXPECT_TRUE(registry()->GetProgressRingAnimationForKey(key));
       EXPECT_EQ(ring_callback_call_count, index + 1u);
     }
@@ -180,9 +178,8 @@
   // Count progress icon animation changed events.
   size_t callback_call_count = 0u;
   auto subscription = registry()->AddProgressIconAnimationChangedCallbackForKey(
-      &key, base::BindLambdaForTesting([&](HoldingSpaceProgressIconAnimation*) {
-        ++callback_call_count;
-      }));
+      &key, base::BindLambdaForTesting(
+                [&](ProgressIconAnimation*) { ++callback_call_count; }));
 
   // Unset progress icon animation for `key`.
   EXPECT_FALSE(registry()->SetProgressIconAnimationForKey(&key, nullptr));
@@ -190,7 +187,7 @@
   EXPECT_EQ(callback_call_count, 0u);
 
   // Create a progress icon `animation`.
-  auto animation = std::make_unique<HoldingSpaceProgressIconAnimation>();
+  auto animation = std::make_unique<ProgressIconAnimation>();
   auto* animation_ptr = animation.get();
 
   // Set progress icon `animation` for `key`.
@@ -214,9 +211,8 @@
   // Count progress ring animation changed events.
   size_t callback_call_count = 0u;
   auto subscription = registry()->AddProgressRingAnimationChangedCallbackForKey(
-      &key, base::BindLambdaForTesting([&](HoldingSpaceProgressRingAnimation*) {
-        ++callback_call_count;
-      }));
+      &key, base::BindLambdaForTesting(
+                [&](ProgressRingAnimation*) { ++callback_call_count; }));
 
   // Unset progress ring animation for `key`.
   EXPECT_FALSE(registry()->SetProgressRingAnimationForKey(&key, nullptr));
@@ -224,8 +220,8 @@
   EXPECT_EQ(callback_call_count, 0u);
 
   // Create a progress ring `animation`.
-  auto animation = HoldingSpaceProgressRingAnimation::CreateOfType(
-      HoldingSpaceProgressRingAnimation::Type::kPulse);
+  auto animation =
+      ProgressRingAnimation::CreateOfType(ProgressRingAnimation::Type::kPulse);
   auto* animation_ptr = animation.get();
 
   // Set progress ring `animation` for `key`.
diff --git a/ash/system/progress_indicator/progress_ring_animation.cc b/ash/system/progress_indicator/progress_ring_animation.cc
new file mode 100644
index 0000000..b9594ec
--- /dev/null
+++ b/ash/system/progress_indicator/progress_ring_animation.cc
@@ -0,0 +1,35 @@
+// Copyright 2021 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.
+
+#include "ash/system/progress_indicator/progress_ring_animation.h"
+
+#include "ash/system/progress_indicator/progress_ring_indeterminate_animation.h"
+#include "ash/system/progress_indicator/progress_ring_pulse_animation.h"
+
+namespace ash {
+
+ProgressRingAnimation::ProgressRingAnimation(Type type,
+                                             base::TimeDelta duration,
+                                             bool is_cyclic)
+    : ProgressIndicatorAnimation(duration, is_cyclic), type_(type) {}
+
+ProgressRingAnimation::~ProgressRingAnimation() = default;
+
+// static
+std::unique_ptr<ProgressRingAnimation> ProgressRingAnimation::CreateOfType(
+    Type type) {
+  switch (type) {
+    case Type::kIndeterminate:
+      return std::make_unique<ProgressRingIndeterminateAnimation>();
+    case Type::kPulse:
+      return std::make_unique<ProgressRingPulseAnimation>();
+  }
+}
+
+void ProgressRingAnimation::UpdateAnimatableProperties(double fraction) {
+  UpdateAnimatableProperties(fraction, &start_position_, &end_position_,
+                             &opacity_);
+}
+
+}  // namespace ash
diff --git a/ash/system/progress_indicator/progress_ring_animation.h b/ash/system/progress_indicator/progress_ring_animation.h
new file mode 100644
index 0000000..b9a0ecf
--- /dev/null
+++ b/ash/system/progress_indicator/progress_ring_animation.h
@@ -0,0 +1,64 @@
+// Copyright 2021 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.
+
+#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_ANIMATION_H_
+#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_ANIMATION_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/system/progress_indicator/progress_indicator_animation.h"
+
+namespace ash {
+
+// An animation for a `HoldingSpaceProgressIndicator` to be painted in lieu of
+// the determinate progress ring that would otherwise be painted.
+class ASH_EXPORT ProgressRingAnimation : public ProgressIndicatorAnimation {
+ public:
+  enum class Type {
+    kIndeterminate,  // See `ProgressRingIndeterminateAnimation`.
+    kPulse,          // See `ProgressRingPulseAnimation`.
+  };
+
+  ProgressRingAnimation(const ProgressRingAnimation&) = delete;
+  ProgressRingAnimation& operator=(const ProgressRingAnimation&) = delete;
+  ~ProgressRingAnimation() override;
+
+  // Returns a created progress ring animation of the specified `type`.
+  static std::unique_ptr<ProgressRingAnimation> CreateOfType(Type type);
+
+  // Returns the specific type of this animation.
+  Type type() const { return type_; }
+
+  // Returns animatable properties.
+  float start_position() const { return start_position_; }
+  float end_position() const { return end_position_; }
+  float opacity() const { return opacity_; }
+
+ protected:
+  ProgressRingAnimation(Type type, base::TimeDelta duration, bool is_cyclic);
+
+  // Implementing classes should update any desired animatable properties as
+  // appropriate for the specified animation `fraction`.
+  virtual void UpdateAnimatableProperties(double fraction,
+                                          float* start_position,
+                                          float* end_position,
+                                          float* opacity) = 0;
+
+ private:
+  // ProgressIndicatorAnimation:
+  void UpdateAnimatableProperties(double fraction) override;
+
+  // The specific type of this animation.
+  const Type type_;
+
+  // Animatable properties.
+  float start_position_ = 0.f;
+  float end_position_ = 1.f;
+  float opacity_ = 1.f;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.cc b/ash/system/progress_indicator/progress_ring_indeterminate_animation.cc
similarity index 81%
rename from ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.cc
rename to ash/system/progress_indicator/progress_ring_indeterminate_animation.cc
index 65240f5..e45bd6c 100644
--- a/ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.cc
+++ b/ash/system/progress_indicator/progress_ring_indeterminate_animation.cc
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h"
+#include "ash/system/progress_indicator/progress_ring_indeterminate_animation.h"
+
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/paint_throbber.h"
 
@@ -31,18 +32,17 @@
 
 }  // namespace
 
-// HoldingSpaceProgressRingIndeterminateAnimation ------------------------------
+// ProgressRingIndeterminateAnimation ------------------------------------------
 
-HoldingSpaceProgressRingIndeterminateAnimation::
-    HoldingSpaceProgressRingIndeterminateAnimation()
-    : HoldingSpaceProgressRingAnimation(Type::kIndeterminate,
-                                        kAnimationDuration,
-                                        /*is_cyclic=*/true) {}
+ProgressRingIndeterminateAnimation::ProgressRingIndeterminateAnimation()
+    : ProgressRingAnimation(Type::kIndeterminate,
+                            kAnimationDuration,
+                            /*is_cyclic=*/true) {}
 
-HoldingSpaceProgressRingIndeterminateAnimation::
-    ~HoldingSpaceProgressRingIndeterminateAnimation() = default;
+ProgressRingIndeterminateAnimation::~ProgressRingIndeterminateAnimation() =
+    default;
 
-void HoldingSpaceProgressRingIndeterminateAnimation::UpdateAnimatableProperties(
+void ProgressRingIndeterminateAnimation::UpdateAnimatableProperties(
     double fraction,
     float* start_position,
     float* end_position,
diff --git a/ash/system/progress_indicator/progress_ring_indeterminate_animation.h b/ash/system/progress_indicator/progress_ring_indeterminate_animation.h
new file mode 100644
index 0000000..96ca760e
--- /dev/null
+++ b/ash/system/progress_indicator/progress_ring_indeterminate_animation.h
@@ -0,0 +1,34 @@
+// Copyright 2021 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.
+
+#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
+#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
+
+#include "ash/system/progress_indicator/progress_ring_animation.h"
+
+namespace ash {
+
+// An animation for a `HoldingSpaceProgressIndicator` to paint an indeterminate
+// progress ring in lieu of the determinate progress ring that would otherwise
+// be painted.
+class ProgressRingIndeterminateAnimation : public ProgressRingAnimation {
+ public:
+  ProgressRingIndeterminateAnimation();
+  ProgressRingIndeterminateAnimation(
+      const ProgressRingIndeterminateAnimation&) = delete;
+  ProgressRingIndeterminateAnimation& operator=(
+      const ProgressRingIndeterminateAnimation&) = delete;
+  ~ProgressRingIndeterminateAnimation() override;
+
+ private:
+  // ProgressRingAnimation:
+  void UpdateAnimatableProperties(double fraction,
+                                  float* start_value,
+                                  float* end_value,
+                                  float* opacity) override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_INDETERMINATE_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_ring_pulse_animation.cc b/ash/system/progress_indicator/progress_ring_pulse_animation.cc
similarity index 86%
rename from ash/system/holding_space/holding_space_progress_ring_pulse_animation.cc
rename to ash/system/progress_indicator/progress_ring_pulse_animation.cc
index defbf62..4dffa899 100644
--- a/ash/system/holding_space/holding_space_progress_ring_pulse_animation.cc
+++ b/ash/system/progress_indicator/progress_ring_pulse_animation.cc
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/holding_space/holding_space_progress_ring_pulse_animation.h"
+#include "ash/system/progress_indicator/progress_ring_pulse_animation.h"
+
 #include "base/cxx17_backports.h"
 #include "base/dcheck_is_on.h"
 #include "base/notreached.h"
@@ -37,13 +38,12 @@
 
 }  // namespace
 
-// HoldingSpaceProgressRingPulseAnimation --------------------------------------
+// ProgressRingPulseAnimation --------------------------------------------------
 
-HoldingSpaceProgressRingPulseAnimation::HoldingSpaceProgressRingPulseAnimation()
-    : HoldingSpaceProgressRingAnimation(
-          Type::kPulse,
-          base::Milliseconds(kAnimationDurationInMs),
-          /*is_cyclic=*/false) {
+ProgressRingPulseAnimation::ProgressRingPulseAnimation()
+    : ProgressRingAnimation(Type::kPulse,
+                            base::Milliseconds(kAnimationDurationInMs),
+                            /*is_cyclic=*/false) {
 #if DCHECK_IS_ON()
   constexpr size_t kAnimationKeyFramesCount = base::size(kAnimationKeyFrames);
   DCHECK_GE(kAnimationKeyFramesCount, 2u);
@@ -65,10 +65,9 @@
 #endif  // DCHECK_IS_ON()
 }
 
-HoldingSpaceProgressRingPulseAnimation::
-    ~HoldingSpaceProgressRingPulseAnimation() = default;
+ProgressRingPulseAnimation::~ProgressRingPulseAnimation() = default;
 
-void HoldingSpaceProgressRingPulseAnimation::UpdateAnimatableProperties(
+void ProgressRingPulseAnimation::UpdateAnimatableProperties(
     double fraction,
     float* start_position,
     float* end_position,
diff --git a/ash/system/progress_indicator/progress_ring_pulse_animation.h b/ash/system/progress_indicator/progress_ring_pulse_animation.h
new file mode 100644
index 0000000..91caaf5
--- /dev/null
+++ b/ash/system/progress_indicator/progress_ring_pulse_animation.h
@@ -0,0 +1,33 @@
+// Copyright 2021 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.
+
+#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_PULSE_ANIMATION_H_
+#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_PULSE_ANIMATION_H_
+
+#include "ash/system/progress_indicator/progress_ring_animation.h"
+
+namespace ash {
+
+// An animation for a `HoldingSpaceProgressIndicator` to paint a pulsing
+// progress ring in lieu of the determinate progress ring that would otherwise
+// be painted.
+class ProgressRingPulseAnimation : public ProgressRingAnimation {
+ public:
+  ProgressRingPulseAnimation();
+  ProgressRingPulseAnimation(const ProgressRingPulseAnimation&) = delete;
+  ProgressRingPulseAnimation& operator=(const ProgressRingPulseAnimation&) =
+      delete;
+  ~ProgressRingPulseAnimation() override;
+
+ private:
+  // ProgressRingAnimation:
+  void UpdateAnimatableProperties(double fraction,
+                                  float* start_value,
+                                  float* end_value,
+                                  float* opacity) override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_RING_PULSE_ANIMATION_H_
diff --git a/ash/system/time/calendar_month_view.cc b/ash/system/time/calendar_month_view.cc
index 12da14ad..8f29d54 100644
--- a/ash/system/time/calendar_month_view.cc
+++ b/ash/system/time/calendar_month_view.cc
@@ -88,6 +88,7 @@
       date_(date),
       grayed_out_(is_grayed_out_date),
       row_index_(row_index),
+      tool_tip_(base::TimeFormatWithPattern(date, "MMMMdyyyy")),
       calendar_view_controller_(calendar_view_controller) {
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
   SetBorder(views::CreateEmptyBorder(calendar_utils::kDateCellInsets));
@@ -102,7 +103,10 @@
                 gfx::Insets(kFocusCirclePadding)));
 
   DisableFocus();
-
+  if (!grayed_out_) {
+    SetTooltipText(tool_tip_);
+    SetAccessibleName(tool_tip_);
+  }
   scoped_calendar_view_controller_observer_.Observe(calendar_view_controller_);
 }
 
@@ -224,6 +228,11 @@
   SchedulePaint();
 }
 
+void CalendarDateCellView::SetFirstOnFocusedAccessibilityLabel() {
+  SetAccessibleName(l10n_util::GetStringFUTF16(
+      IDS_ASH_CALENDAR_DATE_CELL_ON_FOCUS_ACCESSIBLE_DESCRIPTION, tool_tip_));
+}
+
 gfx::Point CalendarDateCellView::GetEventsPresentIndicatorCenterPosition() {
   const gfx::Rect content = GetContentsBounds();
   return gfx::Point(
@@ -239,16 +248,15 @@
   const int event_number =
       calendar_view_controller_->EventsNumberOfDay(date_,
                                                    /*events =*/nullptr);
+
   const int tooltip_id = (event_number <= 1)
                              ? IDS_ASH_CALENDAR_DATE_CELL_TOOLTIP
                              : IDS_ASH_CALENDAR_DATE_CELL_PLURAL_EVENTS_TOOLTIP;
-
-  SetTooltipText(l10n_util::GetStringFUTF16(
+  tool_tip_ = l10n_util::GetStringFUTF16(
       tooltip_id, base::TimeFormatWithPattern(date_, "MMMMdyyyy"),
-      base::UTF8ToUTF16(base::NumberToString(event_number))));
-  SetAccessibleName(l10n_util::GetStringFUTF16(
-      tooltip_id, base::TimeFormatWithPattern(date_, "MMMMdyyyy"),
-      base::UTF8ToUTF16(base::NumberToString(event_number))));
+      base::UTF8ToUTF16(base::NumberToString(event_number)));
+  SetTooltipText(tool_tip_);
+  SetAccessibleName(tool_tip_);
 
   if (event_number == 0)
     return;
diff --git a/ash/system/time/calendar_month_view.h b/ash/system/time/calendar_month_view.h
index 138ce34b..46b44e22 100644
--- a/ash/system/time/calendar_month_view.h
+++ b/ash/system/time/calendar_month_view.h
@@ -52,6 +52,10 @@
   // Schedule paint the cell to show the event dot.
   void MaybeSchedulePaint();
 
+  // When focusing on the date cell for the first time, it shows "Use arrow keys
+  // to navigate between dates" as instructions.
+  void SetFirstOnFocusedAccessibilityLabel();
+
   // The row index in the date's month view.
   int row_index() const { return row_index_; }
 
@@ -83,6 +87,10 @@
   // If the current cell is selected.
   bool is_selected_ = false;
 
+  // The tool tip for this view. Before events data is back, only show date.
+  // After the events date is back, show date and event numbers.
+  std::u16string tool_tip_;
+
   // Owned by UnifiedCalendarViewController.
   CalendarViewController* const calendar_view_controller_;
 
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 33515095..64e2933 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -582,7 +582,7 @@
     return;
   }
 
-  // When focusing on the `content_view_`, we decide which is the to-be-focued
+  // When focusing on the `content_view_`, we decide which is the to-be-focused
   // cell based on the current position.
   const int position = scroll_view_->GetVisibleRect().y();
   const int row_height = calendar_view_controller_->row_height();
@@ -596,12 +596,14 @@
     while (position > (PositionOfCurrentMonth() + row_index * row_height))
       ++row_index;
 
+    CalendarDateCellView* focused_cell;
     if (current_month_->has_today() && row_index <= today_index) {
-      focus_manager->SetFocusedView(
-          current_month_->focused_cells()[today_index]);
+      focused_cell = current_month_->focused_cells()[today_index];
     } else {
-      focus_manager->SetFocusedView(current_month_->focused_cells()[row_index]);
+      focused_cell = current_month_->focused_cells()[row_index];
     }
+    focused_cell->SetFirstOnFocusedAccessibilityLabel();
+    focus_manager->SetFocusedView(focused_cell);
   } else {
     // If there's no visible row of the current month on the screen, focus on
     // the first visible non-grayed-out date of the next month.
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index cd68a72..c275b5b 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -138,7 +138,13 @@
   base::Time now = base::Time::Now();
   label_->SetText(l10n_util::GetStringFUTF16(
       IDS_ASH_STATUS_TRAY_DATE, FormatDayOfWeek(now), FormatDate(now)));
-  SetAccessibleName(TimeFormatFriendlyDateAndTime(now));
+  if (features::IsCalendarViewEnabled()) {
+    SetAccessibleName(l10n_util::GetStringFUTF16(
+        IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION,
+        TimeFormatFriendlyDateAndTime(now)));
+  } else {
+    SetAccessibleName(TimeFormatFriendlyDateAndTime(now));
+  }
   label_->NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
   NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
 }
diff --git a/ash/webui/diagnostics_ui/resources/text_badge.html b/ash/webui/diagnostics_ui/resources/text_badge.html
index bf72ff1..646b409 100644
--- a/ash/webui/diagnostics_ui/resources/text_badge.html
+++ b/ash/webui/diagnostics_ui/resources/text_badge.html
@@ -1,33 +1,28 @@
 <style include="diagnostics-shared">
   .error {
-    background-color:
-        rgba(var(--google-red-600-rgb), var(--cros-second-tone-opacity));
+    background-color: var(--google-red-50);
     color: var(--cros-text-color-alert);
   }
 
   .queued,
   .skipped,
   .stopped {
-    background-color:
-        rgba(var(--google-grey-600-rgb), var(--cros-second-tone-opacity));
+    background-color: var(--google-grey-200);
     color: var(--cros-text-color-secondary);
   }
 
   .running {
-    background-color:
-        rgba(var(--google-blue-600-rgb), var(--cros-second-tone-opacity));
+    background-color: var(--google-blue-50);
     color: var(--cros-text-color-prominent);
   }
 
   .success {
-    background-color:
-        rgba(var(--google-green-600-rgb), var(--cros-second-tone-opacity));
+    background-color: var(--google-green-50);
     color: var(--cros-text-color-positive);
   }
 
   .warning {
-    background-color:
-        rgba(var(--google-yellow-300-rgb), var(--cros-second-tone-opacity));
+    background-color: var(--google-yellow-50);
     color: var(--cros-text-color-warning);
   }
 
diff --git a/ash/webui/print_management/resources/PRESUBMIT.py b/ash/webui/print_management/resources/PRESUBMIT.py
new file mode 100644
index 0000000..d9901a94
--- /dev/null
+++ b/ash/webui/print_management/resources/PRESUBMIT.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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.
+"""Presubmit script for Print Management.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+USE_PYTHON3 = True
+
+import sys
+
+# Use existing SemanticCssChecker to advise use of cros approved colors
+# and semantic variables which support dark-mode display.
+# See: ui/chromeos/styles/semantic_css_checker.py
+def _CheckSemanticCssColors(input_api, output_api):
+  original_sys_path = sys.path
+  join = input_api.os_path.join
+  src_root = input_api.change.RepositoryRoot()
+  try:
+    # Change the system path to SemanticCssChecker's directory to be
+    # able to import it.
+    sys.path.append(join(src_root, 'ui', 'chromeos', 'styles'))
+    from semantic_css_checker import SemanticCssChecker
+  finally:
+    sys.path = original_sys_path
+
+  return SemanticCssChecker.RunChecks(input_api, output_api)
+
+
+def _CommonChecks(input_api, output_api):
+    """Checks common to both upload and commit."""
+    results = []
+    results.extend(_CheckSemanticCssColors(input_api, output_api))
+    return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return _CommonChecks(input_api, output_api)
diff --git a/ash/webui/scanning/resources/PRESUBMIT.py b/ash/webui/scanning/resources/PRESUBMIT.py
new file mode 100644
index 0000000..cbb31287
--- /dev/null
+++ b/ash/webui/scanning/resources/PRESUBMIT.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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.
+"""Presubmit script for Scanning.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+USE_PYTHON3 = True
+
+import sys
+
+# Use existing SemanticCssChecker to advise use of cros approved colors
+# and semantic variables which support dark-mode display.
+# See: ui/chromeos/styles/semantic_css_checker.py
+def _CheckSemanticCssColors(input_api, output_api):
+  original_sys_path = sys.path
+  join = input_api.os_path.join
+  src_root = input_api.change.RepositoryRoot()
+  try:
+    # Change the system path to SemanticCssChecker's directory to be
+    # able to import it.
+    sys.path.append(join(src_root, 'ui', 'chromeos', 'styles'))
+    from semantic_css_checker import SemanticCssChecker
+  finally:
+    sys.path = original_sys_path
+
+  return SemanticCssChecker.RunChecks(input_api, output_api)
+
+
+def _CommonChecks(input_api, output_api):
+    """Checks common to both upload and commit."""
+    results = []
+    results.extend(_CheckSemanticCssColors(input_api, output_api))
+    return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return _CommonChecks(input_api, output_api)
diff --git a/ash/webui/shimless_rma/resources/calibration_component_chip.html b/ash/webui/shimless_rma/resources/calibration_component_chip.html
index 9207f6f..27a1f09 100644
--- a/ash/webui/shimless_rma/resources/calibration_component_chip.html
+++ b/ash/webui/shimless_rma/resources/calibration_component_chip.html
@@ -9,7 +9,6 @@
   }
 
   #componentButton {
-    align-items: normal;
     border-radius: 4px;
     box-shadow: var(--cr-card-shadow);
     color: var(--shimless-component-text-color);
@@ -18,17 +17,17 @@
     font-weight: var(--shimless-medium-font-weight);
     height: 70px;
     line-height: var(--shimless-component-line-height);
-    margin-bottom: 20px;
-    margin-inline-end: 10px;
-    width: 190px;
+    width: 100%;
   }
 
   #labelDiv {
     color: grey;
     flex-basis: 155px;
+    inset-inline-start: 0;
     margin-bottom: auto;
     margin-top: auto;
     padding-inline-start: 20px;
+    position: absolute;
   }
 
   :host([checked]) #labelDiv {
diff --git a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
index 76390faa..e5e7b92 100644
--- a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
@@ -13,7 +13,7 @@
     </div>
   </div>
   <div slot="right-pane">
-    <div>
+    <div class="component-grid repair">
       <template is="dom-repeat" items="{{componentCheckboxes_}}" as="component">
         <repair-component-chip id="[[component.id]]"
           checked="{{component.checked}}"
diff --git a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
index e49e259..c6cb0a9 100644
--- a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
+++ b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
@@ -16,7 +16,7 @@
     </div>
   </div>
   <div slot="right-pane">
-    <div>
+    <div class="component-grid calibration">
       <template is="dom-repeat" items="{{componentCheckboxes_}}" as="component">
         <calibration-component-chip id="[[component.id]]"
           checked="{{component.checked}}"
diff --git a/ash/webui/shimless_rma/resources/repair_component_chip.html b/ash/webui/shimless_rma/resources/repair_component_chip.html
index fc2e078..d831daf 100644
--- a/ash/webui/shimless_rma/resources/repair_component_chip.html
+++ b/ash/webui/shimless_rma/resources/repair_component_chip.html
@@ -9,7 +9,6 @@
   }
 
   #componentButton {
-    align-items: normal;
     border-radius: 4px;
     box-shadow: var(--cr-card-shadow);
     color: var(--shimless-component-text-color);
@@ -18,17 +17,17 @@
     font-weight: var(--shimless-medium-font-weight);
     height: 70px;
     line-height: var(--shimless-component-line-height);
-    margin-bottom: 20px;
-    margin-inline-end: 10px;
-    width: 190px;
+    width: 100%;
   }
 
   #labelDiv {
     color: grey;
     flex-basis: 155px;
+    inset-inline-start: 0;
     margin-bottom: auto;
     margin-top: auto;
     padding-inline-start: 20px;
+    position: absolute;
   }
 
   :host([checked]) #labelDiv {
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html b/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
index aafb4fcc..b3c8547 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
+++ b/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
@@ -76,5 +76,21 @@
       height: 32px;
       width: 32px;
     }
+
+    .component-grid {
+      display: inline-grid;
+      grid-column-gap: 16px;
+      grid-row-gap: 16px;
+      height: 100%;
+      width: 100%;
+    }
+
+    .component-grid.repair {
+      grid-template-columns: auto auto;
+    }
+
+    .component-grid.calibration {
+      grid-template-columns: auto;
+    }
   </style>
 </template>
diff --git a/ash/wm/desks/persistent_desks_bar_context_menu.cc b/ash/wm/desks/persistent_desks_bar_context_menu.cc
index 06e537c7..1baaa8d4 100644
--- a/ash/wm/desks/persistent_desks_bar_context_menu.cc
+++ b/ash/wm/desks/persistent_desks_bar_context_menu.cc
@@ -63,7 +63,8 @@
       static_cast<int>(CommandId::kFeedBack),
       l10n_util::GetStringUTF16(
           IDS_ASH_PERSISTENT_DESKS_BAR_CONTEXT_MENU_FEEDBACK),
-      ui::ImageModel::FromVectorIcon(kPersistentDesksBarFeedbackIcon));
+      ui::ImageModel::FromVectorIcon(kPersistentDesksBarFeedbackIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
 
   auto* bar_controller = Shell::Get()->persistent_desks_bar_controller();
   const bool is_enabled = bar_controller->IsEnabled();
@@ -75,7 +76,8 @@
               : IDS_ASH_PERSISTENT_DESKS_BAR_CONTEXT_MENU_SHOW_DESKS_BAR),
       ui::ImageModel::FromVectorIcon(is_enabled
                                          ? kPersistentDesksBarNotVisibleIcon
-                                         : kPersistentDesksBarVisibleIcon));
+                                         : kPersistentDesksBarVisibleIcon,
+                                     ui::kColorAshSystemUIMenuIcon));
 
   return context_menu_model_.get();
 }
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index 00c16ee..045c0d7a 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -689,17 +689,50 @@
 // overview item. Regression test for https://crbug.com/1285491.
 TEST_F(DesksTemplatesTest, SaveDeskAsTemplateButtonAligned) {
   // Create a test window in the current desk.
-  auto test_window = CreateAppWindow();
+  auto test_window1 = CreateAppWindow();
+  auto test_window2 = CreateAppWindow();
+  // A widget is needed to close.
+  auto test_widget = CreateTestWidget();
+
   ToggleOverview();
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
   auto* overview_grid =
       GetOverviewSession()->GetGridWithRootWindow(root_window);
   views::Widget* save_desk_as_template_widget =
       GetSaveDeskAsTemplateButtonForRoot(root_window);
-  auto& window_list = overview_grid->window_list();
-  ASSERT_FALSE(window_list.empty());
-  EXPECT_EQ(window_list.front()->target_bounds().x() + kWindowMargin,
-            save_desk_as_template_widget->GetWindowBoundsInScreen().x());
+
+  auto verify_save_desk_widget_bounds = [&overview_grid,
+                                         save_desk_as_template_widget]() {
+    auto& window_list = overview_grid->window_list();
+    ASSERT_FALSE(window_list.empty());
+    EXPECT_EQ(
+        std::round(window_list.front()->target_bounds().x()) + kWindowMargin,
+        save_desk_as_template_widget->GetWindowBoundsInScreen().x());
+    EXPECT_EQ(std::round(window_list.front()->target_bounds().y()) - 40,
+              save_desk_as_template_widget->GetWindowBoundsInScreen().y());
+  };
+
+  verify_save_desk_widget_bounds();
+
+  // Tests that the save desk button remains slightly above the first overview
+  // item after changes to the window position. Regression test for
+  // https://crbug.com/1289020.
+
+  // Delete an overview item and verify.
+  OverviewItem* item = GetOverviewItemForWindow(test_widget->GetNativeWindow());
+  item->CloseWindow();
+
+  // `NativeWidgetAura::Close()` fires a post task.
+  base::RunLoop().RunUntilIdle();
+  verify_save_desk_widget_bounds();
+
+  // Create a new desk to leave zero state and verify.
+  const DesksBarView* desks_bar_view = overview_grid->desks_bar_view();
+  ASSERT_TRUE(desks_bar_view->IsZeroState());
+  auto* new_desk_button = desks_bar_view->zero_state_new_desk_button();
+  ClickOnView(new_desk_button);
+  ASSERT_FALSE(desks_bar_view->IsZeroState());
+  verify_save_desk_widget_bounds();
 }
 
 // Tests that the save desk as template button is disabled when the maximum
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index cbccd500..ea53d42 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1851,8 +1851,18 @@
       !overview_session_->GetCurrentDraggedOverviewItem() &&
       !Shell::Get()->tablet_mode_controller()->InTabletMode() &&
       !IsShowingDesksTemplatesGrid();
-  if (target_visible == IsSaveDeskAsTemplateButtonVisible())
-    return;
+
+  const bool visibility_changed =
+      target_visible != IsSaveDeskAsTemplateButtonVisible();
+
+  // Adds or removes the widget from the accessibility focus order when exiting
+  // the scope. Skip the update if the widget's visibility hasn't changed.
+  base::ScopedClosureRunner update_accessibility_focus(base::BindOnce(
+      [](OverviewSession* session, bool widget_visibility_changed) {
+        if (widget_visibility_changed)
+          session->UpdateAccessibilityFocus();
+      },
+      overview_session_, visibility_changed));
 
   if (!target_visible) {
     if (save_desk_as_template_widget_) {
@@ -1862,7 +1872,6 @@
           base::BindOnce(&OverviewGrid::OnSaveDeskAsTemplateButtonFadedOut,
                          weak_ptr_factory_.GetWeakPtr()));
     }
-    overview_session_->UpdateAccessibilityFocus();
     return;
   }
 
@@ -1898,18 +1907,24 @@
 
   // Set the widget position above the overview item window and default width
   // and height.
-  // TODO: Reposition Desks Templates bounds for tablet mode.
-  const gfx::RectF overview_item_bounds = window_list_.front()->target_bounds();
+  const gfx::Point first_overview_item_origin =
+      gfx::ToRoundedPoint(window_list_.front()->target_bounds().origin());
   const gfx::Size preferred_size =
       save_desk_as_template_widget_->GetContentsView()->GetPreferredSize();
+
+  // Animate the widget so it moves with the items. The widget's size isn't
+  // changing, so its ok to use a bounds animation as opposed to a transform
+  // animation.
+  ScopedOverviewAnimationSettings settings(
+      OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW,
+      save_desk_as_template_widget_->GetNativeWindow());
   save_desk_as_template_widget_->SetBounds(gfx::Rect(
       // Align the widget so it is visually aligned with the first overview
       // item, which has a invisible border of `kWindowMargin` thickness.
-      overview_item_bounds.x() + kWindowMargin,
-      overview_item_bounds.y() - kSaveDeskAsTemplateOverviewItemSpacingDp,
-      preferred_size.width(), preferred_size.height()));
-
-  overview_session_->UpdateAccessibilityFocus();
+      first_overview_item_origin +
+          gfx::Vector2d(kWindowMargin,
+                        -kSaveDeskAsTemplateOverviewItemSpacingDp),
+      preferred_size));
 }
 
 bool OverviewGrid::IsSaveDeskAsTemplateButtonVisible() const {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index d1120aa8..d39a293 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4310,6 +4310,7 @@
       "test/android/javatests/src/org/chromium/base/test/TestTraceEvent.java",
       "test/android/javatests/src/org/chromium/base/test/UiThreadStatement.java",
       "test/android/javatests/src/org/chromium/base/test/UiThreadTest.java",
+      "test/android/javatests/src/org/chromium/base/test/UnitTestNoBrowserProcessHook.java",
       "test/android/javatests/src/org/chromium/base/test/metrics/HistogramTestRule.java",
       "test/android/javatests/src/org/chromium/base/test/params/BaseJUnit4RunnerDelegate.java",
       "test/android/javatests/src/org/chromium/base/test/params/BlockJUnit4RunnerDelegate.java",
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
index a4fd675..2a6465a08 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
@@ -30,6 +30,16 @@
 }
 
 TEST(PartitionAllocAsMalloc, Mallinfo) {
+  // mallinfo was deprecated in glibc 2.33. The Chrome OS device sysroot has
+  // a new-enough glibc, but the Linux one doesn't yet, so we can't switch to
+  // the replacement mallinfo2 yet.
+  // Once we update the Linux sysroot to be new enough, this warning will
+  // start firing on Linux too. At that point, s/mallinfo/mallinfo2/ in this
+  // file and remove the pragma here and and the end of this function.
+#if BUILDFLAG(IS_CHROMEOS)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
   constexpr int kLargeAllocSize = 10 * 1024 * 1024;
   struct mallinfo before = mallinfo();
   void* data = malloc(1000);
@@ -67,6 +77,9 @@
   EXPECT_LT(after_free.hblks, after_alloc.hblks);
   EXPECT_LT(after_free.hblkhd, after_alloc.hblkhd);
   EXPECT_LT(after_free.uordblks, after_alloc.uordblks);
+#if BUILDFLAG(IS_CHROMEOS)
+#pragma clang diagnostic pop
+#endif
 }
 
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/base/android/java/src/org/chromium/base/ContextUtils.java b/base/android/java/src/org/chromium/base/ContextUtils.java
index 6d1625f..b9774bea 100644
--- a/base/android/java/src/org/chromium/base/ContextUtils.java
+++ b/base/android/java/src/org/chromium/base/ContextUtils.java
@@ -6,8 +6,11 @@
 
 import android.app.Activity;
 import android.app.Application;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.res.AssetManager;
 import android.os.Build;
@@ -19,6 +22,7 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.compat.ApiHelperForM;
+import org.chromium.base.compat.ApiHelperForO;
 import org.chromium.build.BuildConfig;
 
 /**
@@ -30,6 +34,15 @@
     private static Context sApplicationContext;
 
     /**
+     * Flag for {@link Context#registerReceiver}: The receiver can receive broadcasts from other
+     * Apps. Has the same behavior as marking a statically registered receiver with "exported=true".
+     *
+     * TODO(mthiesse): Move to ApiHelperForT when we build against T SDK.
+     */
+    public static final int RECEIVER_EXPORTED = 0x2;
+    public static final int RECEIVER_NOT_EXPORTED = 0x4;
+
+    /**
      * Initialization-on-demand holder. This exists for thread-safe lazy initialization.
      */
     private static class Holder {
@@ -176,4 +189,24 @@
 
         return null;
     }
+
+    public static Intent registerExportedBroadcastReceiver(
+            Context context, BroadcastReceiver receiver, IntentFilter filter, String permission) {
+        return registerBroadcastReceiver(context, receiver, filter, permission, RECEIVER_EXPORTED);
+    }
+
+    public static Intent registerNonExportedBroadcastReceiver(
+            Context context, BroadcastReceiver receiver, IntentFilter filter) {
+        return registerBroadcastReceiver(context, receiver, filter, null, RECEIVER_NOT_EXPORTED);
+    }
+
+    private static Intent registerBroadcastReceiver(Context context, BroadcastReceiver receiver,
+            IntentFilter filter, String permission, int flags) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            return ApiHelperForO.registerReceiver(
+                    context, receiver, filter, permission, null, flags);
+        } else {
+            return context.registerReceiver(receiver, filter, permission, null);
+        }
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/compat/ApiHelperForO.java b/base/android/java/src/org/chromium/base/compat/ApiHelperForO.java
index 299fc6a..c0024a4 100644
--- a/base/android/java/src/org/chromium/base/compat/ApiHelperForO.java
+++ b/base/android/java/src/org/chromium/base/compat/ApiHelperForO.java
@@ -8,8 +8,11 @@
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Notification;
+import android.content.BroadcastReceiver;
 import android.content.ClipDescription;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
@@ -141,4 +144,12 @@
     public static String getNotificationChannelId(Notification notification) {
         return notification.getChannelId();
     }
+
+    /**
+     * See {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler, int)}
+     */
+    public static Intent registerReceiver(Context context, BroadcastReceiver receiver,
+            IntentFilter filter, String permission, Handler scheduler, int flags) {
+        return context.registerReceiver(receiver, filter, permission, scheduler, flags);
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index c75777c..697815d 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -83,6 +83,8 @@
     // The singleton instance of LibraryLoader. Never null (not final for tests).
     private static LibraryLoader sInstance = new LibraryLoader();
 
+    private static boolean sBrowserStartupBlockedForTesting;
+
     // One-way switch becomes true when the libraries are initialized (by calling
     // LibraryLoaderJni.get().libraryLoaded, which forwards to LibraryLoaded(...) in
     // library_loader_hooks.cc). Note that this member should remain a one-way switch, since it
@@ -1043,6 +1045,14 @@
         mInitialized = true;
     }
 
+    public static void setBrowserProcessStartupBlockedForTesting() {
+        sBrowserStartupBlockedForTesting = true;
+    }
+
+    public static boolean isBrowserProcessStartupBlockedForTesting() {
+        return sBrowserStartupBlockedForTesting;
+    }
+
     // The native methods below are defined in library_loader_hooks.cc.
     @NativeMethods
     interface Natives {
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index 68269f0..bc88749 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -60,9 +60,8 @@
   TestLastThree(parser.get());
 
   ASSERT_TRUE(value);
-  std::string str;
-  EXPECT_TRUE(value->GetAsString(&str));
-  EXPECT_EQ("test", str);
+  ASSERT_TRUE(value->is_string());
+  EXPECT_EQ("test", value->GetString());
 }
 
 TEST_F(JSONParserTest, ConsumeList) {
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index b8f01ac..f4c71f7 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -81,10 +81,8 @@
   EXPECT_TRUE(root->is_dict());
   root = JSONReader::Read("/* comment */\"sample string\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string value;
-  EXPECT_TRUE(root->GetAsString(&value));
-  EXPECT_EQ("sample string", value);
+  ASSERT_TRUE(root->is_string());
+  EXPECT_EQ("sample string", root->GetString());
   root = JSONReader::Read("[1, /* comment, 2 ] */ \n 3]");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_list());
@@ -246,29 +244,23 @@
 TEST(JSONReaderTest, SimpleString) {
   absl::optional<Value> root = JSONReader::Read("\"hello world\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
-  EXPECT_EQ("hello world", str_val);
+  ASSERT_TRUE(root->is_string());
+  EXPECT_EQ("hello world", root->GetString());
 }
 
 TEST(JSONReaderTest, EmptyString) {
   absl::optional<Value> root = JSONReader::Read("\"\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
-  EXPECT_EQ("", str_val);
+  ASSERT_TRUE(root->is_string());
+  EXPECT_EQ("", root->GetString());
 }
 
 TEST(JSONReaderTest, BasicStringEscapes) {
   absl::optional<Value> root =
       JSONReader::Read("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
-  EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
+  ASSERT_TRUE(root->is_string());
+  EXPECT_EQ(" \"\\/\b\f\n\r\t\v", root->GetString());
 }
 
 TEST(JSONReaderTest, UnicodeEscapes) {
@@ -276,9 +268,8 @@
   absl::optional<Value> root =
       JSONReader::Read("\"\\x41\\xFF\\x00\\u1234\\u0000\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
+  ASSERT_TRUE(root->is_string());
+  const std::string& str_val = root->GetString();
   EXPECT_EQ(std::wstring(L"A\x00FF\0\x1234\0", 5), UTF8ToWide(str_val));
 
   // The contents of a Unicode escape may only be four hex chars. Previously the
@@ -555,9 +546,8 @@
 TEST(JSONReaderTest, UTF8Input) {
   absl::optional<Value> root = JSONReader::Read("\"\xe7\xbd\x91\xe9\xa1\xb5\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
+  ASSERT_TRUE(root->is_string());
+  const std::string& str_val = root->GetString();
   EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val));
 
   root = JSONReader::Read("{\"path\": \"/tmp/\xc3\xa0\xc3\xa8\xc3\xb2.png\"}");
@@ -611,9 +601,8 @@
     root = JSONReader::Read(noncharacter);
     ASSERT_TRUE(root);
     ASSERT_TRUE(root->is_string());
-    std::string value;
-    EXPECT_TRUE(root->GetAsString(&value));
-    EXPECT_EQ(std::string(noncharacter + 1, strlen(noncharacter) - 2), value);
+    EXPECT_EQ(std::string(noncharacter + 1, strlen(noncharacter) - 2),
+              root->GetString());
   }
 }
 
@@ -626,20 +615,16 @@
 TEST(JSONReaderTest, UTF16Escapes) {
   absl::optional<Value> root = JSONReader::Read("\"\\u20ac3,14\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  std::string str_val;
-  EXPECT_TRUE(root->GetAsString(&str_val));
+  ASSERT_TRUE(root->is_string());
   EXPECT_EQ(
       "\xe2\x82\xac"
       "3,14",
-      str_val);
+      root->GetString());
 
   root = JSONReader::Read("\"\\ud83d\\udca9\\ud83d\\udc6c\"");
   ASSERT_TRUE(root);
-  EXPECT_TRUE(root->is_string());
-  str_val.clear();
-  EXPECT_TRUE(root->GetAsString(&str_val));
-  EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val);
+  ASSERT_TRUE(root->is_string());
+  EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", root->GetString());
 }
 
 TEST(JSONReaderTest, InvalidUTF16Escapes) {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
index 06acc8f..187bfa3 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java
@@ -199,7 +199,7 @@
      */
     @CallSuper
     protected List<TestHook> getPreTestHooks() {
-        return Arrays.asList(CommandLineFlags.getPreTestHook());
+        return Arrays.asList(CommandLineFlags.getPreTestHook(), new UnitTestNoBrowserProcessHook());
     }
 
     /**
diff --git a/base/test/android/javatests/src/org/chromium/base/test/UnitTestNoBrowserProcessHook.java b/base/test/android/javatests/src/org/chromium/base/test/UnitTestNoBrowserProcessHook.java
new file mode 100644
index 0000000..55edf8f
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/UnitTestNoBrowserProcessHook.java
@@ -0,0 +1,28 @@
+// Copyright 2022 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.
+
+package org.chromium.base.test;
+
+import android.content.Context;
+
+import org.junit.runners.model.FrameworkMethod;
+
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.test.BaseJUnit4ClassRunner.TestHook;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.RequiresRestart;
+
+/**
+ * PreTestHook used to ensure we don't start the browser process in unit tests.
+ * */
+public final class UnitTestNoBrowserProcessHook implements TestHook {
+    @Override
+    public void run(Context targetContext, FrameworkMethod testMethod) {
+        Batch annotation = testMethod.getDeclaringClass().getAnnotation(Batch.class);
+        if (annotation != null && annotation.value().equals(Batch.UNIT_TESTS)) {
+            if (testMethod.getAnnotation(RequiresRestart.class) != null) return;
+            LibraryLoader.setBrowserProcessStartupBlockedForTesting();
+        }
+    }
+}
diff --git a/base/values.cc b/base/values.cc
index 726ad12c..1cb7ce3 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -824,22 +824,6 @@
   return is_string();
 }
 
-bool Value::GetAsString(const Value** out_value) const {
-  if (out_value && is_string()) {
-    *out_value = this;
-    return true;
-  }
-  return is_string();
-}
-
-bool Value::GetAsString(StringPiece* out_value) const {
-  if (out_value && is_string()) {
-    *out_value = GetString();
-    return true;
-  }
-  return is_string();
-}
-
 bool Value::GetAsList(ListValue** out_value) {
   if (out_value && is_list()) {
     *out_value = static_cast<ListValue*>(this);
@@ -1199,7 +1183,10 @@
   if (!Get(path, &value))
     return false;
 
-  return value->GetAsString(out_value);
+  const bool is_string = value->is_string();
+  if (is_string && out_value)
+    *out_value = value->GetString();
+  return is_string;
 }
 
 bool DictionaryValue::GetString(StringPiece path,
@@ -1208,7 +1195,10 @@
   if (!Get(path, &value))
     return false;
 
-  return value->GetAsString(out_value);
+  const bool is_string = value->is_string();
+  if (is_string && out_value)
+    *out_value = UTF8ToUTF16(value->GetString());
+  return is_string;
 }
 
 bool DictionaryValue::GetStringASCII(StringPiece path,
diff --git a/base/values.h b/base/values.h
index 5ccb7ca..5bd69fd 100644
--- a/base/values.h
+++ b/base/values.h
@@ -546,8 +546,6 @@
   // DEPRECATED, use `GetIfString()` instead.
   bool GetAsString(std::string* out_value) const;
   bool GetAsString(std::u16string* out_value) const;
-  bool GetAsString(const Value** out_value) const;
-  bool GetAsString(StringPiece* out_value) const;
   // ListValue::From is the equivalent for std::unique_ptr conversions.
   // DEPRECATED, use `is_list()` instead.
   bool GetAsList(ListValue** out_value);
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 2f12b38..f77817c 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -1443,28 +1443,11 @@
   ASSERT_TRUE(utf16_value.get());
   ASSERT_TRUE(utf16_value->is_string());
 
-  // Test overloaded GetAsString.
-  std::string narrow = "http://google.com";
-  std::u16string utf16 = u"http://google.com";
-  const Value* string_value = nullptr;
-  ASSERT_TRUE(narrow_value->GetAsString(&narrow));
-  ASSERT_TRUE(narrow_value->GetAsString(&utf16));
-  ASSERT_TRUE(narrow_value->GetAsString(&string_value));
-  ASSERT_EQ(std::string("narrow"), narrow);
-  ASSERT_EQ(u"narrow", utf16);
-  ASSERT_EQ(string_value->GetString(), narrow);
+  ASSERT_TRUE(narrow_value->is_string());
+  ASSERT_EQ(std::string("narrow"), narrow_value->GetString());
 
-  ASSERT_TRUE(utf16_value->GetAsString(&narrow));
-  ASSERT_TRUE(utf16_value->GetAsString(&utf16));
-  ASSERT_TRUE(utf16_value->GetAsString(&string_value));
-  ASSERT_EQ(std::string("utf16"), narrow);
-  ASSERT_EQ(u"utf16", utf16);
-  ASSERT_EQ(string_value->GetString(), narrow);
-
-  // Don't choke on NULL values.
-  ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::u16string*>(nullptr)));
-  ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(nullptr)));
-  ASSERT_TRUE(narrow_value->GetAsString(static_cast<const Value**>(nullptr)));
+  ASSERT_TRUE(utf16_value->is_string());
+  ASSERT_EQ(std::string("utf16"), utf16_value->GetString());
 }
 
 TEST(ValuesTest, ListDeletion) {
@@ -1649,22 +1632,14 @@
   ASSERT_TRUE(copy_string);
   ASSERT_NE(copy_string, string_weak);
   ASSERT_TRUE(copy_string->is_string());
-  std::string copy_string_value;
-  std::u16string copy_string16_value;
-  ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
-  ASSERT_TRUE(copy_string->GetAsString(&copy_string16_value));
-  ASSERT_EQ(std::string("hello"), copy_string_value);
-  ASSERT_EQ(u"hello", copy_string16_value);
+  ASSERT_EQ(std::string("hello"), copy_string->GetString());
 
   Value* copy_string16 = nullptr;
   ASSERT_TRUE(copy_dict->Get("string16", &copy_string16));
   ASSERT_TRUE(copy_string16);
   ASSERT_NE(copy_string16, string16_weak);
   ASSERT_TRUE(copy_string16->is_string());
-  ASSERT_TRUE(copy_string16->GetAsString(&copy_string_value));
-  ASSERT_TRUE(copy_string16->GetAsString(&copy_string16_value));
-  ASSERT_EQ(std::string("hello16"), copy_string_value);
-  ASSERT_EQ(u"hello16", copy_string16_value);
+  ASSERT_EQ(std::string("hello16"), copy_string16->GetString());
 
   Value* copy_binary = nullptr;
   ASSERT_TRUE(copy_dict->Get("binary", &copy_binary));
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index c3d18a3..e1ff750 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -239,7 +239,15 @@
     for dirpath, _, filenames in os.walk(path):
       for f in filenames:
         artifact_path = os.path.join(dirpath, f)
-        artifacts[os.path.relpath(artifact_path, path)] = {
+        artifact_id = os.path.relpath(artifact_path, path)
+        # Some artifacts will have non-Latin characters in the filename, eg:
+        # 'ui_tree_Chinese Pinyin-你好.txt'. ResultDB's API rejects such
+        # characters as an artifact ID, so force the file name down into ascii.
+        # For more info, see:
+        # https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/resultdb/proto/v1/artifact.proto;drc=3bff13b8037ca76ec19f9810033d914af7ec67cb;l=46
+        artifact_id = artifact_id.encode('ascii', 'replace').decode()
+        artifact_id = artifact_id.replace('\\', '?')
+        artifacts[artifact_id] = {
             'filePath': artifact_path,
         }
     return artifacts
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 33eed25b..55d38c7 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220204.1.1
+7.20220204.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 33eed25b..55d38c7 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220204.1.1
+7.20220204.2.1
diff --git a/buildtools/third_party/libunwind/BUILD.gn b/buildtools/third_party/libunwind/BUILD.gn
index 36f1d9b..b53aa46c 100644
--- a/buildtools/third_party/libunwind/BUILD.gn
+++ b/buildtools/third_party/libunwind/BUILD.gn
@@ -17,13 +17,6 @@
     # unwind its own frames.
     "-funwind-tables",
   ]
-
-  if (is_fuchsia) {
-    # Workaround for https://bugs.llvm.org/show_bug.cgi?id=45875:
-    # compiling the C code with -fexceptions ensures that accurate
-    # unwinding information is generated for _Unwind_RaiseException.
-    cflags_c = [ "-fexceptions" ]
-  }
 }
 
 source_set("libunwind") {
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 9266f95..de32016 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -448,7 +448,6 @@
   "java/res/drawable/capture_overlay_border.xml",
   "java/res/drawable/checkerboard_background.xml",
   "java/res/drawable/computer_black_24dp.xml",
-  "java/res/drawable/contextual_search_panel_help_background.xml",
   "java/res/drawable/custom_tabs_handle_view_shape.xml",
   "java/res/drawable/devices_black_24dp.xml",
   "java/res/drawable/discover_card.xml",
@@ -493,7 +492,6 @@
   "java/res/drawable/ic_sync_off_48dp.xml",
   "java/res/drawable/ic_sync_on_48dp.xml",
   "java/res/drawable/ic_toolbar_share_offset_24dp.xml",
-  "java/res/drawable/ic_touch_app_blue.xml",
   "java/res/drawable/ic_translate.xml",
   "java/res/drawable/ic_trending_down_black.xml",
   "java/res/drawable/ic_tv_options_input_settings_rotated_grey.xml",
@@ -584,7 +582,6 @@
   "java/res/layout/contextual_search_caption_view.xml",
   "java/res/layout/contextual_search_card_icon_view.xml",
   "java/res/layout/contextual_search_context_view.xml",
-  "java/res/layout/contextual_search_panel_help_view.xml",
   "java/res/layout/contextual_search_promo_view.xml",
   "java/res/layout/contextual_search_promo_view_revised.xml",
   "java/res/layout/contextual_search_quick_action_icon_view.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index b0f14065..a4ad9f0 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -304,7 +304,6 @@
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchImageControl.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java",
-  "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelHelp.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPromoControl.java",
@@ -385,7 +384,6 @@
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristic.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java",
-  "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersister.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index 9309a2d..2c45472 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -2493,14 +2493,6 @@
         id="VectorRaster"
         message="">
         <location
-            file="../../chrome/android/java/res/drawable/ic_touch_app_blue.xml"
-            line="14"/>
-    </issue>
-
-    <issue
-        id="VectorRaster"
-        message="">
-        <location
             file="../../chrome/android/java/res/drawable/ic_translate.xml"
             line="14"/>
     </issue>
diff --git a/chrome/android/java/res/drawable/contextual_search_panel_help_background.xml b/chrome/android/java/res/drawable/contextual_search_panel_help_background.xml
deleted file mode 100644
index 7cab720..0000000
--- a/chrome/android/java/res/drawable/contextual_search_panel_help_background.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 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. -->
-
-<!-- This background insets another background by a fixed amount.
-     The inset amount comes from the UX design and is crafted to match
-     the inset of the rounded frame of individual search results displayed
-     in the search results page. No bottom inset is needed because the
-     next thing shown (usually the content) has its own inset. -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/hairline_border_card_background"
-    android:insetTop="6dp"
-    android:insetLeft="6dp"
-    android:insetRight="6dp"/>
-
diff --git a/chrome/android/java/res/drawable/ic_touch_app_blue.xml b/chrome/android/java/res/drawable/ic_touch_app_blue.xml
deleted file mode 100644
index e3dcc46..0000000
--- a/chrome/android/java/res/drawable/ic_touch_app_blue.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 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. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:fillColor="@macro/default_icon_color_accent1"
-        android:pathData="M18.19,12.44l-3.24-1.62c1.29-1,2.12-2.56,2.12-4.32c0-3.03-2.47-5.5-5.5-5.5s-5.5,2.47-5.5,5.5c0,2.13,1.22,3.98,3,4.89 v3.26c-2.11-0.45-2.01-0.44-2.26-0.44c-0.53,0-1.03,0.21-1.41,0.59L4,16.22l5.09,5.09C9.52,21.75,10.12,22,10.74,22h6.3 c0.98,0,1.81-0.7,1.97-1.67l0.8-4.71C20.03,14.32,19.38,13.04,18.19,12.44z M17.84,15.29L17.04,20h-6.3 c-0.09,0-0.17-0.04-0.24-0.1l-3.68-3.68l4.25,0.89V6.5c0-0.28,0.22-0.5,0.5-0.5c0.28,0,0.5,0.22,0.5,0.5v6h1.76l3.46,1.73 C17.69,14.43,17.91,14.86,17.84,15.29z M8.07,6.5c0-1.93,1.57-3.5,3.5-3.5s3.5,1.57,3.5,3.5c0,0.95-0.38,1.81-1,2.44V6.5 c0-1.38-1.12-2.5-2.5-2.5c-1.38,0-2.5,1.12-2.5,2.5v2.44C8.45,8.31,8.07,7.45,8.07,6.5z" />
-</vector>
diff --git a/chrome/android/java/res/layout/contextual_search_panel_help_view.xml b/chrome/android/java/res/layout/contextual_search_panel_help_view.xml
deleted file mode 100644
index 910cb8d..0000000
--- a/chrome/android/java/res/layout/contextual_search_panel_help_view.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 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. -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/contextual_search_panel_help"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    android:visibility="invisible"
-    android:background="@drawable/contextual_search_panel_help_background">
-
-    <ImageView
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_marginTop="12dp"
-        android:layout_marginStart="12dp"
-        android:layout_marginEnd="12dp"
-        app:srcCompat="@drawable/ic_touch_app_blue"
-        android:importantForAccessibility="no" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <org.chromium.ui.widget.TextViewWithLeading
-            android:id="@+id/contextual_search_help_header_view"
-            android:text="@string/contextual_search_help_header"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="12dp"
-            style="@style/TextAppearance.TextLarge.Primary"
-            app:leading="@dimen/headline_size_leading" />
-        <org.chromium.ui.widget.TextViewWithLeading
-            android:id="@+id/contextual_search_help_body_view"
-            android:text="@string/contextual_search_help_body"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="12dp"
-            android:layout_marginBottom="12dp"
-            android:layout_marginEnd="12dp"
-            android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
-            app:leading="@dimen/text_size_small_leading" />
-        <org.chromium.ui.widget.ButtonCompat
-            android:id="@+id/contextual_search_ok_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end"
-            android:layout_marginEnd="18dp"
-            android:layout_marginBottom="12dp"
-            android:text="@string/ok"
-            style="@style/FilledButton.Flat" />
-    </LinearLayout>
-</LinearLayout>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 9c443d7..d73c4f3 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -55,7 +55,6 @@
     <dimen name="contextual_search_chip_list_side_padding">16dp</dimen>
     <!-- Padding at the end of the Bar. -->
     <dimen name="contextual_search_end_padding">7dp</dimen>
-    <dimen name="contextual_search_bubble_y_inset">-3dp</dimen>
     <!-- Padding amount that should be removed between the Related Searches chips carousel
          in the Bar and the content above it due to overlapping whitespace margins. This
          effectively removes the whitespace from the top of the chips carousel because there's
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index d6fee887..b2fd3b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -72,15 +72,6 @@
         void onPromoOptOut();
     }
 
-    /** The interface that the help section uses to communicate with this Panel. */
-    interface ContextualSearchHelpSectionHost extends ContextualSearchPanelSectionHost {
-        /** Returns whether the help section of the panel is enabled for the current user. */
-        boolean isPanelHelpEnabled();
-
-        /** Notifies that the user has clicked the OK button in the help section of the panel. */
-        void onPanelHelpOkClicked();
-    }
-
     /** The interface that the Related Searches section uses to communicate with this Panel. */
     interface RelatedSearchesSectionHost extends ContextualSearchPanelSectionHost {
         /**
@@ -206,7 +197,7 @@
             RectF viewport, RectF visibleViewport, ResourceManager resourceManager, float yOffset) {
         super.getUpdatedSceneOverlayTree(viewport, visibleViewport, resourceManager, yOffset);
         mSceneLayer.update(resourceManager, this, getSearchBarControl(), getBarBannerControl(),
-                getPromoControl(), getPanelHelp(), getRelatedSearchesInBarControl(),
+                getPromoControl(), getRelatedSearchesInBarControl(),
                 getRelatedSearchesInContentControl(), getImageControl());
 
         return mSceneLayer;
@@ -395,7 +386,6 @@
     protected void destroyComponents() {
         super.destroyComponents();
         destroyPromoControl();
-        destroyPanelHelp();
         destroyInBarRelatedSearchesControl();
         destroyInContentRelatedSearchesControl();
         destroyBarBannerControl();
@@ -451,8 +441,8 @@
 
     @Override
     public float getContentY() {
-        return getOffsetY() + getBarContainerHeight() + getPanelHelpHeight()
-                + getRelatedSearchesHeightDps(false) + getPromoHeightPx() * mPxToDp;
+        return getOffsetY() + getBarContainerHeight() + getRelatedSearchesHeightDps(false)
+                + getPromoHeightPx() * mPxToDp;
     }
 
     @Override
@@ -534,15 +524,6 @@
     }
 
     @Override
-    public void setIsPanelHelpActive(boolean isActive) {
-        if (isActive) {
-            getPanelHelp().show();
-        } else {
-            getPanelHelp().hide();
-        }
-    }
-
-    @Override
     public void clearRelatedSearches() {
         getRelatedSearchesInBarControl().hide();
         getRelatedSearchesInContentControl().hide();
@@ -858,7 +839,6 @@
         super.updatePanelForCloseOrPeek(percentage);
 
         getPromoControl().onUpdateFromCloseToPeek(percentage);
-        getPanelHelp().onUpdateFromCloseToPeek(percentage);
         getRelatedSearchesInBarControl().onUpdateFromCloseToPeek(percentage);
         getRelatedSearchesInContentControl().onUpdateFromCloseToPeek(percentage);
         getBarBannerControl().onUpdateFromCloseToPeek(percentage);
@@ -870,7 +850,6 @@
         super.updatePanelForExpansion(percentage);
 
         getPromoControl().onUpdateFromPeekToExpand(percentage);
-        getPanelHelp().onUpdateFromPeekToExpand(percentage);
         getRelatedSearchesInBarControl().onUpdateFromPeekToExpand(percentage);
         getRelatedSearchesInContentControl().onUpdateFromPeekToExpand(percentage);
         getBarBannerControl().onUpdateFromPeekToExpand(percentage);
@@ -882,7 +861,6 @@
         super.updatePanelForMaximization(percentage);
 
         getPromoControl().onUpdateFromExpandToMaximize(percentage);
-        getPanelHelp().onUpdateFromExpandToMaximize(percentage);
         getRelatedSearchesInBarControl().onUpdateFromExpandToMaximize(percentage);
         getRelatedSearchesInContentControl().onUpdateFromExpandToMaximize(percentage);
         getBarBannerControl().onUpdateFromExpandToMaximize(percentage);
@@ -894,9 +872,6 @@
         if (getPromoControl().isVisible()) {
             getPromoControl().invalidate(true);
         }
-        if (getPanelHelp().isVisible()) {
-            getPanelHelp().invalidate(true);
-        }
         if (getRelatedSearchesInBarControl().isVisible()) {
             getRelatedSearchesInBarControl().invalidate(true);
         }
@@ -915,8 +890,6 @@
         updateBasePageTargetY();
 
         super.updatePanelForSizeChange();
-
-        mManagementDelegate.onPanelResized();
     }
 
     @Override
@@ -1111,9 +1084,8 @@
                 @Override
                 public float getYPositionPx() {
                     // Needs to enumerate anything that can appear above it in the panel.
-                    return Math.round(
-                            (getOffsetY() + getBarContainerHeight()
-                                    + getRelatedSearchesHeightDps(false) + getPanelHelpHeight())
+                    return Math.round((getOffsetY() + getBarContainerHeight()
+                                              + getRelatedSearchesHeightDps(false))
                             / mPxToDp);
                 }
 
@@ -1129,7 +1101,6 @@
                         getOverlayPanelContent().showContent();
                         expandPanel(StateChangeReason.OPTIN);
                     }
-                    mManagementDelegate.onPromoOptIn();
                 }
 
                 @Override
@@ -1146,75 +1117,6 @@
     }
 
     // ============================================================================================
-    // In-Panel Help
-    // ============================================================================================
-
-    /** The Help section of the Panel. */
-    private ContextualSearchPanelHelp mPanelHelp;
-    private ContextualSearchHelpSectionHost mHelpSectionHost;
-
-    /**
-     * Returns the {@link ContextualSearchPanelHelp} that controls the help section of this
-     * panel.
-     */
-    private ContextualSearchPanelHelp getPanelHelp() {
-        if (mPanelHelp == null) {
-            mPanelHelp = new ContextualSearchPanelHelp(this, getContextualSearchHelpSectionHost(),
-                    mContext, mContainerView, mResourceLoader);
-        }
-        return mPanelHelp;
-    }
-
-    /**
-     * @return Height of the help section of the panel in DPs.
-     */
-    private float getPanelHelpHeight() {
-        return getPanelHelp().getHeightPx() * mPxToDp;
-    }
-
-    /** Destroys the Help section of this panel. */
-    private void destroyPanelHelp() {
-        mPanelHelp.destroy();
-        mPanelHelp = null;
-    }
-
-    /**
-     * @return An implementation of {@link ContextualSearchHelpSectionHost}.
-     */
-    private ContextualSearchHelpSectionHost getContextualSearchHelpSectionHost() {
-        if (mHelpSectionHost == null) {
-            mHelpSectionHost = new ContextualSearchHelpSectionHost() {
-                @Override
-                public float getYPositionPx() {
-                    // Needs to enumerate anything that can appear above it in the panel.
-                    return Math.round((getOffsetY() + getBarContainerHeight()
-                                              + getRelatedSearchesHeightDps(false))
-                            / mPxToDp);
-                }
-
-                @Override
-                public void onPanelSectionSizeChange(boolean hasStarted) {
-                    // The help section is causing movement which means the promo below
-                    // it will move.
-                    getPromoControl().onUpdateForMovement(hasStarted);
-                }
-
-                @Override
-                public boolean isPanelHelpEnabled() {
-                    return mManagementDelegate.isPanelHelpEnabled();
-                }
-
-                @Override
-                public void onPanelHelpOkClicked() {
-                    // Tell the manager the user said OK.
-                    mManagementDelegate.onPanelHelpOkClicked();
-                }
-            };
-        }
-        return mHelpSectionHost;
-    }
-
-    // ============================================================================================
     // The Delayed Intelligence Feature support
     // ============================================================================================
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
index c1d667a..bf64f1c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
@@ -217,9 +217,6 @@
     }
 
     @Override
-    public void setIsPanelHelpActive(boolean isActive) {}
-
-    @Override
     public void clearRelatedSearches() {}
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelHelp.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelHelp.java
deleted file mode 100644
index b8621a6..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelHelp.java
+++ /dev/null
@@ -1,445 +0,0 @@
-// Copyright 2020 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.
-
-package org.chromium.chrome.browser.compositor.bottombar.contextualsearch;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import androidx.annotation.Nullable;
-
-import org.chromium.base.MathUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeSemanticColorUtils;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelAnimation;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelInflater;
-import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.ContextualSearchHelpSectionHost;
-import org.chromium.chrome.browser.layouts.animation.CompositorAnimator;
-import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
-
-/**
- * Controls a section of the Panel that provides end user help messages.
- * This class is implemented along the lines of {@code ContextualSearchPromoControl} and shares
- * some of it's resources.
- */
-public class ContextualSearchPanelHelp {
-    private static final int INVALID_VIEW_ID = 0;
-
-    private final boolean mIsEnabled;
-    private final OverlayPanel mOverlayPanel;
-    private final Context mContext;
-
-    /** The pixel density. */
-    private final float mDpToPx;
-
-    /** The background color of the view's container to use for native rendering. */
-    private final int mContainerBackgroundColor;
-
-    /**
-     * The inflated View, or {@code null} if the associated Feature is not enabled,
-     * or {@link #destroy} has been called.
-     */
-    @Nullable
-    private HelpControlView mHelpControlView;
-
-    /** Whether the view is visible. */
-    private boolean mIsVisible;
-
-    /** The opacity of the view. */
-    private float mOpacity;
-
-    /** The height of the view in pixels. */
-    private float mHeightPx;
-
-    /** The height of the content in pixels. */
-    private float mContentHeightPx;
-
-    /** Whether the view is showing. */
-    private boolean mIsShowingView;
-
-    /** The Y position of the view. */
-    private float mViewY;
-
-    /** Whether the View was in a state that could be interacted. */
-    private boolean mWasInteractive;
-
-    /** The reference to the help section host. */
-    private ContextualSearchHelpSectionHost mHelpSectionHost;
-
-    /**
-     * @param panel             The panel.
-     * @param helpSectionHost   A reference the host of this section for notifications.
-     * @param context           The Android Context used to inflate the View.
-     * @param container         The container View used to inflate the View.
-     * @param resourceLoader    The resource loader that will handle the snapshot capturing.
-     */
-    ContextualSearchPanelHelp(OverlayPanel panel, ContextualSearchHelpSectionHost helpSectionHost,
-            Context context, ViewGroup container, DynamicResourceLoader resourceLoader) {
-        mIsEnabled = helpSectionHost.isPanelHelpEnabled();
-        mDpToPx = context.getResources().getDisplayMetrics().density;
-        // We match the Opt-in promo background color so the views are seamless when together.
-        mContainerBackgroundColor =
-                ChromeSemanticColorUtils.getContextualSearchPromoBackgroundColor(context);
-
-        mOverlayPanel = panel;
-        mContext = context;
-
-        mHelpControlView =
-                mIsEnabled ? new HelpControlView(panel, context, container, resourceLoader) : null;
-        mHelpSectionHost = helpSectionHost;
-    }
-
-    // ============================================================================================
-    // Public API
-    // ============================================================================================
-
-    /**
-     * @return Whether the View is visible.
-     */
-    public boolean isVisible() {
-        return mIsVisible;
-    }
-
-    /**
-     * @return The View height in pixels.
-     */
-    public float getHeightPx() {
-        return mIsVisible ? mHeightPx : 0f;
-    }
-
-    /** Returns the ID of the help view, or {@code INVALID_VIEW_ID} if there's no view. */
-    public int getViewId() {
-        return mHelpControlView != null ? mHelpControlView.getViewId() : INVALID_VIEW_ID;
-    }
-
-    /**
-     * @return The View opacity.
-     */
-    public float getOpacity() {
-        return mOpacity;
-    }
-
-    /**
-     * @return The background color of the View's container, which includes areas outside the
-     *         content.
-     */
-    public int getContainerBackgroundColor() {
-        return mContainerBackgroundColor;
-    }
-
-    // ============================================================================================
-    // Package-private API
-    // ============================================================================================
-
-    /**
-     * Shows the View. This includes inflating the View and setting its initial state.
-     */
-    void show() {
-        if (mIsVisible || !mIsEnabled) return;
-
-        // Invalidates the View in order to generate a snapshot, but do not show the View yet.
-        // The View should only be displayed when in the expanded state.
-        if (mHelpControlView != null) mHelpControlView.invalidate();
-
-        mIsVisible = true;
-        mWasInteractive = false;
-
-        mHeightPx = mContentHeightPx;
-    }
-
-    /**
-     * Hides the View
-     */
-    void hide() {
-        if (!mIsVisible) return;
-
-        hideView();
-
-        mIsVisible = false;
-
-        mHeightPx = 0.f;
-        mOpacity = 0.f;
-    }
-
-    /**
-     * @return Whether the View reached a state in which it could be interactive.
-     */
-    boolean wasInteractive() {
-        // TODO(donnd): call this and record metrics for whether the user interacted.
-        return mWasInteractive;
-    }
-
-    // ============================================================================================
-    // Panel Animation
-    // ============================================================================================
-
-    /**
-     * Interpolates the UI from states Closed to Peeked.
-     *
-     * @param percentage The completion percentage.
-     */
-    void onUpdateFromCloseToPeek(float percentage) {
-        if (!isVisible()) return;
-
-        // The View snapshot should be fully visible here.
-        updateAppearance(1.0f);
-
-        // The View should not be visible in this state.
-        hideView();
-    }
-
-    /**
-     * Interpolates the UI from states Peeked to Expanded.
-     *
-     * @param percentage The completion percentage.
-     */
-    void onUpdateFromPeekToExpand(float percentage) {
-        updateViewAndNativeAppearance(percentage);
-    }
-
-    /**
-     * Interpolates the UI from states Expanded to Maximized.
-     *
-     * @param percentage The completion percentage.
-     */
-    void onUpdateFromExpandToMaximize(float percentage) {
-        updateViewAndNativeAppearance(percentage);
-    }
-
-    /**
-     * Destroys as much of this instance as possible.
-     */
-    void destroy() {
-        if (mHelpControlView != null) mHelpControlView.destroy();
-        mHelpControlView = null;
-    }
-
-    /** Invalidates the help view. */
-    void invalidate(boolean didViewSizeChange) {
-        if (mHelpControlView != null) mHelpControlView.invalidate(didViewSizeChange);
-    }
-
-    /**
-     * Updates the Android View's hidden state and the native appearance attributes.
-     * This hides the Android View when transitioning between states and instead shows the
-     * native snapshot (by setting the native attributes to do so).
-     * @param percentage The completion percentage.
-     */
-    private void updateViewAndNativeAppearance(float percentage) {
-        if (!isVisible()) return;
-
-        // The panel stays full sized during this size transition.
-        updateAppearance(1.0f);
-
-        // We should show the View only when the Panel has reached the exact height.
-        // If not exact then the non-interactive native code draws the Panel during the transition.
-        if (percentage == 1.f) {
-            showView();
-        } else {
-            hideView();
-        }
-    }
-
-    // ============================================================================================
-    // View Acceptance Animation
-    // ============================================================================================
-
-    /**
-     * Collapses the View in an animated fashion.
-     */
-    void collapse() {
-        if (!mIsShowingView) return;
-
-        hideView();
-
-        // Notify the host that the content is moving so adjustments can be made (e.g. the Opt-in
-        // promo will be in motion when it's shown).
-        mHelpSectionHost.onPanelSectionSizeChange(true);
-
-        CompositorAnimator collapse =
-                CompositorAnimator.ofFloat(mOverlayPanel.getAnimationHandler(), 1.f, 0.f,
-                        OverlayPanelAnimation.BASE_ANIMATION_DURATION_MS, null);
-
-        collapse.addUpdateListener(animator -> updateAppearance(animator.getAnimatedValue()));
-
-        collapse.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                hide();
-                mHelpSectionHost.onPanelSectionSizeChange(false);
-            }
-        });
-
-        collapse.start();
-    }
-
-    /**
-     * Updates the appearance of the View.
-     * @param percentage The completion percentage. 0.f means the view is fully collapsed and
-     *        transparent. 1.f means the view is fully expanded and opaque.
-     */
-    private void updateAppearance(float percentage) {
-        if (mIsVisible) {
-            mHeightPx = Math.round(
-                    MathUtils.clamp(percentage * mContentHeightPx, 0.f, mContentHeightPx));
-            mOpacity = 1.f;
-        } else {
-            mHeightPx = 0.f;
-            mOpacity = 0.f;
-        }
-    }
-
-    // ============================================================================================
-    // Helpers
-    // ============================================================================================
-
-    /**
-     * Shows the Android View. By making the Android View visible, we are allowing the
-     * View to be interactive. Since snapshots are not interactive (they are just a bitmap),
-     * we need to temporarily show the Android View on top of the snapshot, so the user will
-     * be able to click in the View button.
-     */
-    private void showView() {
-        if (mHelpControlView == null) return;
-
-        float y = mHelpSectionHost.getYPositionPx();
-        View view = mHelpControlView.getHelpView();
-        if (view == null || !mIsVisible || (mIsShowingView && mViewY == y) || mHeightPx == 0.f) {
-            return;
-        }
-
-        float offsetX = mOverlayPanel.getOffsetX() * mDpToPx;
-        if (LocalizationUtils.isLayoutRtl()) {
-            offsetX = -offsetX;
-        }
-
-        view.setTranslationX(offsetX);
-        view.setTranslationY(y);
-        view.setVisibility(View.VISIBLE);
-
-        // NOTE: We need to call requestLayout, otherwise the View will not become visible.
-        view.requestLayout();
-
-        mIsShowingView = true;
-        mViewY = y;
-
-        // The View can only be interactive when it is being displayed.
-        mWasInteractive = true;
-    }
-
-    /**
-     * Hides the Android View. See {@link #showView()}.
-     */
-    private void hideView() {
-        if (mHelpControlView == null) return;
-
-        View view = mHelpControlView.getHelpView();
-        if (view == null || !mIsVisible || !mIsShowingView) {
-            return;
-        }
-
-        view.setVisibility(View.INVISIBLE);
-
-        mIsShowingView = false;
-    }
-
-    /**
-     * Calculates the content height of the View, and adjusts the height of the View while
-     * preserving the proportion of the height with the content height. This should be called
-     * whenever the the size of the View changes.
-     */
-    private void calculateHeight() {
-        if (mHelpControlView == null) return;
-
-        mHelpControlView.layoutView();
-
-        final float previousContentHeight = mContentHeightPx;
-        mContentHeightPx = mHelpControlView.getMeasuredHeight();
-
-        if (mIsVisible) {
-            // Calculates the ratio between the current height and the previous content height,
-            // and uses it to calculate the new height, while preserving the ratio.
-            final float ratio = mHeightPx / previousContentHeight;
-            mHeightPx = Math.round(mContentHeightPx * ratio);
-        }
-    }
-
-    /** Called when the OK button has been clicked. */
-    private void onOkButtonClicked() {
-        mHelpSectionHost.onPanelHelpOkClicked();
-        collapse();
-    }
-
-    // ============================================================================================
-    // HelpControlView - the View that this class delegates to.
-    // ============================================================================================
-
-    /**
-     * The {@code HelpControlView} is an {@link OverlayPanelInflater} controlled view that renders
-     * the actual help View and can be created and destroyed under the control of the enclosing
-     * Panel Help class. The enclosing class delegates several public mehthods to this class, e.g.
-     * {@link #invalidate}.
-     */
-    private class HelpControlView extends OverlayPanelInflater {
-        /**
-         * Constructs a help view that can be shown in the panel.
-         * @param panel             The panel.
-         * @param context           The Android Context used to inflate the View.
-         * @param container         The container View used to inflate the View.
-         * @param resourceLoader    The resource loader that will handle the snapshot capturing.
-         */
-        HelpControlView(OverlayPanel panel, Context context, ViewGroup container,
-                DynamicResourceLoader resourceLoader) {
-            super(panel, R.layout.contextual_search_panel_help_view,
-                    R.id.contextual_search_panel_help, context, container, resourceLoader);
-        }
-
-        View getHelpView() {
-            return super.getView();
-        }
-
-        void layoutView() {
-            super.layout();
-        }
-
-        @Override
-        public void destroy() {
-            hide();
-            super.destroy();
-        }
-
-        @Override
-        public void invalidate(boolean didViewSizeChange) {
-            super.invalidate(didViewSizeChange);
-
-            if (didViewSizeChange) {
-                calculateHeight();
-            }
-        }
-
-        @Override
-        protected void onFinishInflate() {
-            super.onFinishInflate();
-
-            View view = getView();
-
-            // "OK" button.
-            Button button = (Button) view.findViewById(R.id.contextual_search_ok_button);
-            button.setOnClickListener(v -> onOkButtonClicked());
-
-            calculateHeight();
-        }
-
-        @Override
-        protected boolean shouldDetachViewAfterCapturing() {
-            return false;
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
index 8243ed34..7669a25a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
@@ -56,7 +56,6 @@
     ContextualSearchBarControl getSearchBarControl();
     ContextualSearchPanelMetrics getPanelMetrics();
     Rect getPanelRect();
-    void setIsPanelHelpActive(boolean isActive);
     void clearRelatedSearches();
 
     /** {@link OverlayPanel} methods */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java
index e25fc98..ff8b843f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java
@@ -8,7 +8,6 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchHeuristics;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchIPH;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchInteractionRecorder;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchUma;
 import org.chromium.chrome.browser.contextualsearch.EngagementSuppression;
@@ -162,10 +161,6 @@
                 ContextualSearchUma.logAllSearches(/* wasRelatedSearches */ false);
             }
 
-            // Notifications to Feature Engagement.
-            ContextualSearchIPH.doSearchFinishedNotifications(profile, mWasSearchContentViewSeen,
-                    mWasActivatedByTap, mWasContextualCardsDataShown);
-
             writeInteractionOutcomesAndReset();
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index 4a9da669..278c8a78 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -12,7 +12,6 @@
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchBarControl;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchImageControl;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel;
-import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelHelp;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPromoControl;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.RelatedSearchesControl;
 import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer;
@@ -52,14 +51,12 @@
      *        in the Bar to facilitate one-click searching.
      * @param relatedSearchesInContentControl A control that displays Related Searches suggestions
      *        in the panel content area to facilitate one-click searching.
-     * @param helpControl A control for the help section of the panel that promotes modified
-     *        user usage and appears below the Bar and above the content.
      * @param imageControl The object controlling the image displayed in the Bar.
      */
     public void update(ResourceManager resourceManager, ContextualSearchPanel panel,
             ContextualSearchBarControl searchBarControl,
             ContextualSearchBarBannerControl barBannerControl,
-            ContextualSearchPromoControl promoControl, ContextualSearchPanelHelp helpControl,
+            ContextualSearchPromoControl promoControl,
             RelatedSearchesControl relatedSearchesInBarControl,
             RelatedSearchesControl relatedSearchesInContentControl,
             ContextualSearchImageControl imageControl) {
@@ -88,13 +85,6 @@
         float searchPromoOpacity = promoControl.getOpacity();
         int searchPromoBackgroundColor = promoControl.getBackgroundColor();
 
-        // Panel Help section
-        int panelHelpViewId = helpControl.getViewId();
-        boolean panelHelpVisible = helpControl.isVisible();
-        float panelHelpHeightPx = helpControl.getHeightPx();
-        float panelHelpOpacity = helpControl.getOpacity();
-        int panelHelpContainerBackgroundColor = helpControl.getContainerBackgroundColor();
-
         // Related Searches section
         int relatedSearchesInContentViewId = relatedSearchesInContentControl.getViewId();
         boolean relatedSearchesInContentVisible = relatedSearchesInContentControl.isVisible();
@@ -181,9 +171,6 @@
                 panel.getTabHeight() * mDpToPx, panel.getBasePageBrightness(),
                 panel.getBasePageY() * mDpToPx, panelWebContents, searchPromoVisible,
                 searchPromoHeightPx, searchPromoOpacity, searchPromoBackgroundColor,
-                // Panel Help
-                panelHelpViewId, panelHelpVisible, panelHelpHeightPx, panelHelpOpacity,
-                panelHelpContainerBackgroundColor,
                 // Related Searches
                 relatedSearchesInContentViewId, relatedSearchesInContentVisible,
                 relatedSearchesInContentHeightPx, relatedSearchesInBarViewId,
@@ -264,9 +251,6 @@
                 float basePageBrightness, float basePageYOffset, WebContents webContents,
                 boolean searchPromoVisible, float searchPromoHeight, float searchPromoOpacity,
                 int searchPromoBackgroundColor,
-                // Panel Help
-                int panelHelpResourceId, boolean panelHelpVisible, float panelHelpHeight,
-                float panelHelpOpacity, int panelHelpBackgroundColor,
                 // Related Searches
                 int relatedSearchesInContentResourceId, boolean relatedSearchesInContentVisible,
                 float relatedSearchesInContentHeight, int relatedSearchesInBarResourceId,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java
deleted file mode 100644
index 26c9528a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java
+++ /dev/null
@@ -1,397 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.contextualsearch;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.PopupWindow.OnDismissListener;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel;
-import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelInterface;
-import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
-import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
-import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.components.feature_engagement.FeatureConstants;
-import org.chromium.components.feature_engagement.Tracker;
-import org.chromium.components.feature_engagement.TriggerState;
-import org.chromium.ui.widget.AnchoredPopupWindow;
-import org.chromium.ui.widget.RectProvider;
-
-/**
- * Helper class for displaying In-Product Help UI for Contextual Search.
- */
-public class ContextualSearchIPH {
-    private static final int FLOATING_BUBBLE_SPACING_FACTOR = 10;
-    private View mParentView;
-    private ContextualSearchPanelInterface mSearchPanel;
-    private TextBubble mHelpBubble;
-    private RectProvider mRectProvider;
-    private String mFeatureName;
-    private boolean mIsShowing;
-    private boolean mIsShowingInPanel;
-    private boolean mDidShow;
-    private boolean mIsPositionedByPanel;
-    private boolean mHasUserEverEngaged;
-    private Point mFloatingBubbleAnchorPoint;
-    private OnDismissListener mDismissListener;
-    private boolean mDidUserOptIn;
-
-    /**
-     * Constructs the helper class.
-     */
-    ContextualSearchIPH() {}
-
-    /**
-     * @param searchPanel The instance of {@link ContextualSearchPanel}.
-     */
-    void setSearchPanel(ContextualSearchPanelInterface searchPanel) {
-        mSearchPanel = searchPanel;
-    }
-
-    /**
-     * @param parentView The parent view that the {@link TextBubble} will be attached to.
-     */
-    void setParentView(View parentView) {
-        mParentView = parentView;
-    }
-
-    /**
-     * Called after the Contextual Search panel's animation is finished.
-     * @param wasActivatedByTap Whether Contextual Search was activated by tapping.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    void onPanelFinishedShowing(boolean wasActivatedByTap, Profile profile) {
-        if (!wasActivatedByTap) {
-            maybeShow(FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_TAP_FEATURE, profile);
-            maybeShow(FeatureConstants.CONTEXTUAL_SEARCH_WEB_SEARCH_FEATURE, profile);
-        }
-    }
-
-    /**
-     * Called after entity data is received.
-     * @param wasActivatedByTap Whether Contextual Search was activated by tapping.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    void onEntityDataReceived(boolean wasActivatedByTap, Profile profile) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        tracker.notifyEvent(EventConstants.CONTEXTUAL_SEARCH_ENTITY_RESULT);
-        if (wasActivatedByTap) {
-            maybeShow(FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_PANEL_OPEN_FEATURE, profile);
-        }
-    }
-
-    /**
-     * Called when the Search Panel is shown.
-     * @param wasActivatedByTap Whether Contextual Search was activated by tapping.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    void onSearchPanelShown(boolean wasActivatedByTap, Profile profile) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        tracker.notifyEvent(wasActivatedByTap
-                        ? EventConstants.CONTEXTUAL_SEARCH_TRIGGERED_BY_TAP
-                        : EventConstants.CONTEXTUAL_SEARCH_TRIGGERED_BY_LONGPRESS);
-
-        // Log whether IPH for tapping has been shown before.
-        if (wasActivatedByTap) {
-            ContextualSearchUma.logTapIPH(
-                    tracker.getTriggerState(FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_TAP_FEATURE)
-                    == TriggerState.HAS_BEEN_DISPLAYED);
-        }
-    }
-
-    /**
-     * Should be called after the user taps but a tap will not trigger due to longpress activation.
-     * @param profile The active user profile.
-     * @param bubbleAnchorPoint The point where the bubble arrow should be positioned.
-     * @param hasUserEverEngaged Whether the user has ever engaged Contextual Search by opening
-     *        the panel.
-     * @param dismissListener An {@link OnDismissListener} to call when the bubble is dismissed.
-     */
-    void onNonTriggeringTap(Profile profile, Point bubbleAnchorPoint, boolean hasUserEverEngaged,
-            OnDismissListener dismissListener) {
-        mFloatingBubbleAnchorPoint = bubbleAnchorPoint;
-        mHasUserEverEngaged = hasUserEverEngaged;
-        mDismissListener = dismissListener;
-        maybeShow(FeatureConstants.CONTEXTUAL_SEARCH_TAPPED_BUT_SHOULD_LONGPRESS_FEATURE, profile,
-                false);
-    }
-
-    /**
-     * Should be called when the panel is shown and a Translation is needed but the user has
-     * not yet Opted-in.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    void onTranslationNeeded(Profile profile) {
-        maybeShow(FeatureConstants.CONTEXTUAL_SEARCH_TRANSLATION_ENABLE_FEATURE, profile);
-    }
-
-    /**
-     * Shows the appropriate In-Product Help UI if the conditions are met.
-     * @param featureName Name of the feature in IPH, look at {@link FeatureConstants}.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    private void maybeShow(String featureName, Profile profile) {
-        maybeShow(featureName, profile, true);
-    }
-
-    /**
-     * Shows the appropriate In-Product Help UI if the conditions are met.
-     * @param featureName Name of the feature in IPH, look at {@link FeatureConstants}.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     * @param isPositionedByPanel Whether the bubble positioning should be based on the
-     *        panel position instead of floating somewhere on the base page.
-     */
-    private void maybeShow(String featureName, Profile profile, boolean isPositionedByPanel) {
-        mIsPositionedByPanel = isPositionedByPanel;
-        if (mIsShowing || profile == null || mParentView == null
-                || mIsPositionedByPanel && mSearchPanel == null) {
-            return;
-        }
-
-        mFeatureName = featureName;
-        maybeShowFeaturedBubble(profile);
-    }
-
-    /**
-     * Shows a help bubble if the In-Product Help conditions are met.
-     * Private state members are used to determine which message to show in the bubble
-     * and how to position it.
-     * @param profile The {@link Profile} used for {@link TrackerFactory}.
-     */
-    private void maybeShowFeaturedBubble(Profile profile) {
-        if (mIsPositionedByPanel && !mSearchPanel.isShowing()) return;
-
-        final Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        if (!tracker.shouldTriggerHelpUI(mFeatureName)) return;
-
-        int stringId = 0;
-        switch (mFeatureName) {
-            case FeatureConstants.CONTEXTUAL_SEARCH_WEB_SEARCH_FEATURE:
-                stringId = R.string.contextual_search_iph_search_result;
-                break;
-            case FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_PANEL_OPEN_FEATURE:
-                stringId = R.string.contextual_search_iph_entity;
-                break;
-            case FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_TAP_FEATURE:
-                stringId = R.string.contextual_search_iph_tap;
-                break;
-            case FeatureConstants.CONTEXTUAL_SEARCH_TAPPED_BUT_SHOULD_LONGPRESS_FEATURE:
-                // TODO(donnd): put the engaged user variant behind a separate fieldtrial parameter
-                // so we can control it or collapse it later.
-                if (mHasUserEverEngaged) {
-                    stringId = R.string.contextual_search_iph_touch_and_hold_engaged;
-                } else {
-                    stringId = R.string.contextual_search_iph_touch_and_hold;
-                }
-                break;
-            case FeatureConstants.CONTEXTUAL_SEARCH_TRANSLATION_ENABLE_FEATURE:
-                stringId = R.string.contextual_search_iph_enable;
-                break;
-        }
-
-        assert stringId != 0;
-        assert mHelpBubble == null;
-        mRectProvider = new RectProvider(getHelpBubbleAnchorRect());
-        mHelpBubble = new TextBubble(mParentView.getContext(), mParentView, stringId, stringId,
-                mRectProvider, ChromeAccessibilityUtil.get().isAccessibilityEnabled());
-
-        // Set the dismiss logic.
-        mHelpBubble.setDismissOnTouchInteraction(true);
-        mHelpBubble.addOnDismissListener(() -> {
-            tracker.dismissed(mFeatureName);
-            mIsShowing = false;
-            mHelpBubble = null;
-        });
-        if (mDismissListener != null) {
-            mHelpBubble.addOnDismissListener(mDismissListener);
-            mDismissListener = null;
-        }
-
-        maybeSetPreferredOrientation();
-        mHelpBubble.show();
-        mIsShowing = true;
-        mIsShowingInPanel = false;
-        mDidShow = true;
-    }
-
-    /**
-     * Updates the position of the help bubble if it is showing.
-     */
-    void updateBubblePosition() {
-        if (!mIsShowing || mIsShowingInPanel || mHelpBubble == null || !mHelpBubble.isShowing()) {
-            return;
-        }
-        mRectProvider.setRect(getHelpBubbleAnchorRect());
-    }
-
-    /** Returns whether to show the In-Panel-Help and start tracking that IPH. */
-    boolean startShowingInPanelHelp(Profile profile) {
-        boolean shouldShow = TrackerFactory.getTrackerForProfile(profile).shouldTriggerHelpUI(
-                FeatureConstants.CONTEXTUAL_SEARCH_IN_PANEL_HELP_FEATURE);
-        if (shouldShow) {
-            // Start tracking this alternative to the bubble being shown.
-            mFeatureName = FeatureConstants.CONTEXTUAL_SEARCH_IN_PANEL_HELP_FEATURE;
-        }
-        mIsShowing = shouldShow;
-        mIsShowingInPanel = mIsShowing;
-        return shouldShow;
-    }
-
-    /**
-     * @return A {@link Rect} object that represents the appropriate anchor for {@link TextBubble}.
-     */
-    private Rect getHelpBubbleAnchorRect() {
-        int yInsetPx = mParentView.getResources().getDimensionPixelOffset(
-                R.dimen.contextual_search_bubble_y_inset);
-        if (!mIsPositionedByPanel) {
-            // Position the bubble to point to an adjusted tap location, since there's no panel,
-            // just a selected word.  It would be better to point to the rectangle of the selected
-            // word, but that's not easy to get.
-            int adjustFactor = shouldPositionBubbleBelowArrow() ? -1 : 1;
-            int yAdjust = FLOATING_BUBBLE_SPACING_FACTOR * yInsetPx * adjustFactor;
-            return new Rect(mFloatingBubbleAnchorPoint.x, mFloatingBubbleAnchorPoint.y + yAdjust,
-                    mFloatingBubbleAnchorPoint.x, mFloatingBubbleAnchorPoint.y + yAdjust);
-        }
-
-        Rect anchorRect = mSearchPanel.getPanelRect();
-        anchorRect.top -= yInsetPx;
-        return anchorRect;
-    }
-
-    /** Overrides the preferred orientation if the bubble is not anchored to the panel. */
-    private void maybeSetPreferredOrientation() {
-        if (mIsPositionedByPanel) return;
-
-        mHelpBubble.setPreferredVerticalOrientation(shouldPositionBubbleBelowArrow()
-                        ? AnchoredPopupWindow.VerticalOrientation.BELOW
-                        : AnchoredPopupWindow.VerticalOrientation.ABOVE);
-    }
-
-    /** @return whether the bubble should be positioned below it's arrow pointer. */
-    private boolean shouldPositionBubbleBelowArrow() {
-        // The bubble looks best when above the arrow, so we use that for most of the screen,
-        // but needs to appear below the arrow near the top.
-        return mFloatingBubbleAnchorPoint.y < mParentView.getHeight() / 3;
-    }
-
-    /**
-     * Notifies that the search has completed so we can dismiss the In-Product Help UI, etc.
-     * @param profile The current user profile.
-     */
-    void onCloseContextualSearch(Profile profile) {
-        recordOptedInOutcome();
-        if (!mIsShowing || TextUtils.isEmpty(mFeatureName)) return;
-
-        if (mIsShowingInPanel) {
-            assert mHelpBubble == null;
-            dismissInPanelHelp(profile);
-        } else {
-            mHelpBubble.dismiss();
-        }
-
-        mIsShowing = false;
-    }
-
-    /**
-     * @return whether the bubble is currently showing for the tap-where-longpress-needed promo.
-     */
-    boolean isShowingForTappedButShouldLongpress() {
-        return mIsShowing
-                && FeatureConstants.CONTEXTUAL_SEARCH_TAPPED_BUT_SHOULD_LONGPRESS_FEATURE.equals(
-                        mFeatureName);
-    }
-
-    /**
-     * Notifies the Feature Engagement backend and logs UMA metrics.
-     * @param profile The current {@link Profile}.
-     * @param wasSearchContentViewSeen Whether the Contextual Search panel was opened.
-     * @param wasActivatedByTap Whether the Contextual Search was activating by tapping.
-     * @param wasContextualCardsDataShown Whether entity cards were received.
-     */
-    public static void doSearchFinishedNotifications(Profile profile,
-            boolean wasSearchContentViewSeen, boolean wasActivatedByTap,
-            boolean wasContextualCardsDataShown) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        if (wasSearchContentViewSeen) {
-            tracker.notifyEvent(EventConstants.CONTEXTUAL_SEARCH_PANEL_OPENED);
-            tracker.notifyEvent(wasActivatedByTap
-                            ? EventConstants.CONTEXTUAL_SEARCH_PANEL_OPENED_AFTER_TAP
-                            : EventConstants.CONTEXTUAL_SEARCH_PANEL_OPENED_AFTER_LONGPRESS);
-
-            // Log whether IPH for opening the panel has been shown before.
-            ContextualSearchUma.logPanelOpenedIPH(
-                    tracker.getTriggerState(
-                            FeatureConstants.CONTEXTUAL_SEARCH_PROMOTE_PANEL_OPEN_FEATURE)
-                    == TriggerState.HAS_BEEN_DISPLAYED);
-
-            // Log whether IPH for Contextual Search web search has been shown before.
-            ContextualSearchUma.logContextualSearchIPH(
-                    tracker.getTriggerState(FeatureConstants.CONTEXTUAL_SEARCH_WEB_SEARCH_FEATURE)
-                    == TriggerState.HAS_BEEN_DISPLAYED);
-        }
-        if (wasContextualCardsDataShown) {
-            tracker.notifyEvent(EventConstants.CONTEXTUAL_SEARCH_PANEL_OPENED_FOR_ENTITY);
-        }
-
-        // Log whether a Translation Opt-in suggestion IPH was ever shown.
-        ContextualSearchUma.logTranslationsOptInIPHShown(
-                tracker.getTriggerState(
-                        FeatureConstants.CONTEXTUAL_SEARCH_TRANSLATION_ENABLE_FEATURE)
-                == TriggerState.HAS_BEEN_DISPLAYED);
-    }
-
-    /**
-     * Notifies the Feature Engagement backend and logs UMA metrics when the user Opted-in.
-     * @param profile The current user {@link Profile}.
-     */
-    void doUserOptedInNotifications(Profile profile) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        tracker.notifyEvent(EventConstants.CONTEXTUAL_SEARCH_ENABLED_OPT_IN);
-        mDidUserOptIn = true;
-    }
-
-    /**
-     * Handles notification that the OK button was pressed on the In-Panel-Help view.
-     * @param profile The current user profile.
-     */
-    void onPanelHelpOkClicked(Profile profile) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-        tracker.notifyEvent(EventConstants.CONTEXTUAL_SEARCH_ACKNOWLEDGED_IN_PANEL_HELP);
-        // Although tracker logs a user action too, it's OK to duplicate it with a Contextual
-        // Search specific user action for ease of discovery in the analysis tool.
-        ContextualSearchUma.logInPanelHelpAcknowledged();
-    }
-
-    /**
-     * Dismisses In-Panel-Help from the tracker so it knows that the help is no longer shown.
-     * @param profile The current user profile.
-     */
-    private void dismissInPanelHelp(Profile profile) {
-        if (EventConstants.CONTEXTUAL_SEARCH_ACKNOWLEDGED_IN_PANEL_HELP.equals(mFeatureName)) {
-            Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-            tracker.dismissed(mFeatureName);
-            mFeatureName = null;
-        }
-    }
-
-    /**
-     * Records UMA metrics indicated whether the user Opted-in.
-     */
-    private void recordOptedInOutcome() {
-        // If we showed the suggestion to Opt-in for Translations, Log whether the user did or not.
-        if (mDidShow
-                && mFeatureName.equals(
-                        FeatureConstants.CONTEXTUAL_SEARCH_TRANSLATION_ENABLE_FEATURE)) {
-            ContextualSearchUma.logTranslationsOptInIPHWorked(mDidUserOptIn);
-        }
-        mDidShow = false;
-        mDidUserOptIn = false;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagementDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagementDelegate.java
index 9a032c9..9e4b9dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagementDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagementDelegate.java
@@ -85,24 +85,6 @@
     void onPanelFinishedShowing();
 
     /**
-     * Called when the Contextual Search panel is resized.
-     */
-    void onPanelResized();
-
-    /**
-     * Called when the privacy Opt-in in the panel has been accepted.
-     */
-    void onPromoOptIn();
-
-    /** Returns whether the In-Panel-Help is currently enabled. */
-    boolean isPanelHelpEnabled();
-
-    /**
-     * Called when the Help section of the panel has its OK button clicked.
-     */
-    void onPanelHelpOkClicked();
-
-    /**
      * Notifies that a Related Searches suggestion has been clicked, and whether it was shown in the
      * Bar or the content area of the Panel.
      * @param suggestionIndex The 0-based index into the list of suggestions provided by the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index a55942b3..a2d7213 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.contextualsearch;
 
 import android.app.Activity;
-import android.graphics.Point;
 import android.net.Uri;
 import android.os.Handler;
 import android.text.TextUtils;
@@ -51,7 +50,6 @@
 import org.chromium.chrome.browser.gsa.GSAContextDisplaySelection;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.layouts.SceneOverlay;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
@@ -143,7 +141,6 @@
     @VisibleForTesting
     protected final ContextualSearchTranslation mTranslateController;
     private final ContextualSearchSelectionClient mContextualSearchSelectionClient;
-    private final ContextualSearchIPH mInProductHelp;
 
     private final ScrimCoordinator mScrimCoordinator;
 
@@ -320,7 +317,6 @@
                 mPolicy, getContextualSearchInternalStateHandler());
         mInteractionRecorder = new ContextualSearchRankerLoggerImpl();
         mContextualSearchSelectionClient = new ContextualSearchSelectionClient();
-        mInProductHelp = new ContextualSearchIPH();
     }
 
     /**
@@ -366,8 +362,6 @@
             mLayoutManager.addSceneOverlay((SceneOverlay) panel);
         }
 
-        mInProductHelp.setParentView(parentView);
-
         mRedirectHandler = RedirectHandler.create();
 
         mIsShowingPromo = false;
@@ -407,7 +401,6 @@
         assert panel != null;
         mSearchPanel = panel;
         mPolicy.setContextualSearchPanel(panel);
-        mInProductHelp.setSearchPanel(panel);
     }
 
     @Override
@@ -502,8 +495,6 @@
         mRelatedSearches = null;
         mIsRelatedSearchesSerp = false;
 
-        mInProductHelp.onCloseContextualSearch(Profile.getLastUsedRegularProfile());
-
         if (mIsShowingPromo && !mDidLogPromoOutcome && mSearchPanel.wasPromoInteractive()) {
             ContextualSearchUma.logPromoOutcome(mWasActivatedByTap, mIsMandatoryPromo);
             mDidLogPromoOutcome = true;
@@ -511,7 +502,6 @@
 
         mIsShowingPromo = false;
         mSearchPanel.setIsPromoActive(false, false);
-        mSearchPanel.setIsPanelHelpActive(false);
         mSearchPanel.clearRelatedSearches();
         notifyHideContextualSearch();
     }
@@ -590,15 +580,11 @@
             mSearchPanel.setIsPromoActive(true, mIsMandatoryPromo);
             mSearchPanel.setDidSearchInvolvePromo();
         }
-        mSearchPanel.setIsPanelHelpActive(true);
 
         mSearchPanel.requestPanelShow(stateChangeReason);
 
         assert mSelectionController.getSelectionType() != SelectionType.UNDETERMINED;
         mWasActivatedByTap = mSelectionController.getSelectionType() == SelectionType.TAP;
-
-        mInProductHelp.onSearchPanelShown(
-                mWasActivatedByTap, Profile.fromWebContents(mTabSupplier.get().getWebContents()));
     }
 
     @Override
@@ -874,12 +860,6 @@
         boolean quickActionShown =
                 mSearchPanel.getSearchBarControl().getQuickActionControl().hasQuickAction();
         mReceivedContextualCardsEntityData = !quickActionShown && receivedCaptionOrThumbnail;
-
-        if (mReceivedContextualCardsEntityData) {
-            mInProductHelp.onEntityDataReceived(
-                    mWasActivatedByTap, Profile.getLastUsedRegularProfile());
-        }
-
         ContextualSearchUma.logContextualCardsDataShown(mReceivedContextualCardsEntityData);
         mSearchPanel.getPanelMetrics().setWasContextualCardsDataShown(
                 mReceivedContextualCardsEntityData, resolvedSearchTerm.cardTagEnum());
@@ -1435,44 +1415,15 @@
 
     @Override
     public void onPanelFinishedShowing() {
-        Profile profile = Profile.getLastUsedRegularProfile();
-        mInProductHelp.onPanelFinishedShowing(mWasActivatedByTap, profile);
-        // Try to figure out the language of the selection and show an IPH if a translation
-        // is needed.
-        if (mContext != null && mPolicy.isUserUndecided()
-                && mTranslateController.needsTranslation(mContext.getDetectedLanguage())) {
-            mInProductHelp.onTranslationNeeded(profile);
-        }
         resetStateAfterSearch();
     }
 
     @Override
-    public void onPanelResized() {
-        mInProductHelp.updateBubblePosition();
-    }
-
-    @Override
     public ScrimCoordinator getScrimCoordinator() {
         return mScrimCoordinator;
     }
 
     @Override
-    public void onPromoOptIn() {
-        mInProductHelp.doUserOptedInNotifications(Profile.getLastUsedRegularProfile());
-    }
-
-    @Override
-    public boolean isPanelHelpEnabled() {
-        return mPolicy.isLongpressInPanelHelpCondition()
-                && mInProductHelp.startShowingInPanelHelp(Profile.getLastUsedRegularProfile());
-    }
-
-    @Override
-    public void onPanelHelpOkClicked() {
-        mInProductHelp.onPanelHelpOkClicked(Profile.getLastUsedRegularProfile());
-    }
-
-    @Override
     public void onRelatedSearchesSuggestionClicked(int suggestionIndex, boolean isInBarSuggestion) {
         boolean showDefaultSearch = isInBarSuggestion
                 ? ContextualSearchFieldTrial.showDefaultChipInBar()
@@ -1554,9 +1505,8 @@
                 return;
             }
 
-            // Process normally unless something went wrong with the selection or an IPH triggered
-            // on tap when promoting longpress, otherwise just finish up.
-            if (result != null && !mInProductHelp.isShowingForTappedButShouldLongpress()) {
+            // Process normally unless something went wrong with the selection.
+            if (result != null) {
                 assert mContext != null;
                 mContext.onSelectionAdjusted(
                         result.getExtendedStartAdjust(), result.getExtendedEndAdjust());
@@ -1679,21 +1629,9 @@
     }
 
     @Override
-    public void handleValidTap(int x, int y) {
+    public void handleValidTap() {
         if (isSuppressed()) return;
 
-        if (!mPolicy.isTapSupported() && mPolicy.canResolveLongpress()) {
-            // User tapped when Longpress is needed.  Convert location to screen coordinates, and
-            // put up some in-product help.
-            int yOffset = (int) mBrowserControlsStateProvider.getTopVisibleContentOffset();
-            int parentScreenXy[] = new int[2];
-            mParentView.getLocationInWindow(parentScreenXy);
-            mInProductHelp.onNonTriggeringTap(Profile.getLastUsedRegularProfile(),
-                    new Point(x + parentScreenXy[0], y + yOffset + parentScreenXy[1]),
-                    new CtrSuppression().getPrevious28DayCtr() > 0,
-                    () -> mSelectionController.clearSelection());
-        }
-
         // This will synchronously advance to the next state (and possibly others) before
         // returning.
         mInternalStateController.enter(InternalState.TAP_RECOGNIZED);
@@ -1821,12 +1759,8 @@
                 if (isSearchPanelShowing()) {
                     mSearchPanel.closePanel(reason, false);
                 } else {
-                    // Also clear any tap-based selection unless the Tap IPH is showing. In the
-                    // latter case we preserve the selection so the help bubble has something to
-                    // point to.
-                    if (!mPolicy.isLiteralSearchTapEnabled()
-                            && mSelectionController.getSelectionType() == SelectionType.TAP
-                            && !mInProductHelp.isShowingForTappedButShouldLongpress()) {
+                    // Clear any tap-based selection, but not longpress based selections.
+                    if (mSelectionController.getSelectionType() == SelectionType.TAP) {
                         mSelectionController.clearSelection();
                     }
                 }
@@ -1863,7 +1797,6 @@
             public void tapGestureCommit() {
                 mInternalStateController.notifyStartingWorkOn(InternalState.TAP_GESTURE_COMMIT);
                 if (!mPolicy.isTapSupported()
-                                && !mInProductHelp.isShowingForTappedButShouldLongpress()
                         || mSelectionController.getSelectionType()
                                 == SelectionType.RESOLVING_LONG_PRESS) {
                     hideContextualSearch(StateChangeReason.UNKNOWN);
@@ -1886,16 +1819,6 @@
             @Override
             public void decideSuppression() {
                 mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION);
-
-                // We may have gotten here even without Tap being supported if an IPH for Tap
-                // is active.  In that case we want to be sure to show, so skip the suppression
-                // decision.
-                if (mInProductHelp.isShowingForTappedButShouldLongpress()) {
-                    mInternalStateController.notifyFinishedWorkOn(
-                            InternalState.DECIDING_SUPPRESSION);
-                    return;
-                }
-
                 // TODO(donnd): Move handleShouldSuppressTap out of the Selection Controller.
                 mSelectionController.handleShouldSuppressTap(mContext, mInteractionRecorder);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index 0b6a8390..6da17a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -472,7 +472,7 @@
             mY = y;
             mFontSizeDips = fontSizeDips;
             mTextRunLength = textRunLength;
-            mHandler.handleValidTap(x, y);
+            mHandler.handleValidTap();
         } else {
             // Long press, or long-press selection handles shown; reset last tap state.
             mLastTapState = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
index cdc6c45..36065542 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
@@ -28,10 +28,8 @@
 
     /**
      * Handle a valid tap gesture on the base page.
-     * @param x The x-coordinate of the tap in pixels.
-     * @param y The y-coordinate of the tap in pixels.
      */
-    public void handleValidTap(int x, int y);
+    public void handleValidTap();
 
     /**
      * Handle an invalid tap gesture on the base page.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
index e00f1218..ac79d5b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
@@ -784,54 +784,6 @@
     }
 
     /**
-     * When Contextual Search panel is opened, logs whether In-Product Help for opening the panel
-     * was ever shown.
-     * @param wasIPHShown Whether In-Product help was shown.
-     */
-    public static void logPanelOpenedIPH(boolean wasIPHShown) {
-        RecordHistogram.recordBooleanHistogram(
-                "Search.ContextualSearchPanelOpenedIPHShown", wasIPHShown);
-    }
-
-    /**
-     * When Contextual Search panel is opened, logs whether In-Product Help for Contextual Search
-     * was ever shown.
-     * @param wasIPHShown Whether In-Product help was shown.
-     */
-    public static void logContextualSearchIPH(boolean wasIPHShown) {
-        RecordHistogram.recordBooleanHistogram("Search.ContextualSearchIPHShown", wasIPHShown);
-    }
-
-    /**
-     * When Contextual Search is triggered by tapping, logs whether In-Product Help for tapping was
-     * ever shown.
-     * @param wasIPHShown Whether In-Product help was shown.
-     */
-    public static void logTapIPH(boolean wasIPHShown) {
-        RecordHistogram.recordBooleanHistogram("Search.ContextualSearchTapIPHShown", wasIPHShown);
-    }
-
-    /**
-     * Logs whether we have ever shown an In-Product Help for Translations suggesting that the user
-     * Opt-in.
-     * @param wasIPHShown Whether In-Product help was shown.
-     */
-    public static void logTranslationsOptInIPHShown(boolean wasIPHShown) {
-        RecordHistogram.recordBooleanHistogram(
-                "Search.ContextualSearch.TranslationsOptInIPHShown", wasIPHShown);
-    }
-
-    /**
-     * Logs whether the user actually did opt-in after seeing the In-Product Help for Translations
-     * suggesting that the user should Opt-in.
-     * @param didOptIn Whether the user did opt-in.
-     */
-    public static void logTranslationsOptInIPHWorked(boolean didOptIn) {
-        RecordHistogram.recordBooleanHistogram(
-                "Search.ContextualSearch.TranslationsOptInIPHWorked", didOptIn);
-    }
-
-    /**
      * Logs a user action for the duration of viewing the panel that describes the amount of time
      * the user viewed the bar and panel overall.
      * @param durationMs The duration to record.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
index 66f3042f..5081700 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
@@ -28,7 +28,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UrlUtils;
@@ -150,7 +149,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291159")
+    @FlakyTest(message = "https://crbug.com/1291159")
     public void testAutocompleteUpdatedOnSetText() {
         // Verify that setting a new string will clear the autocomplete.
         mOmnibox.setText("test");
@@ -205,7 +204,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291968")
+    @FlakyTest(message = "https://crbug.com/1291968")
     public void testAutocompleteUpdatedOnSelection() throws TimeoutException {
         // Verify that setting a selection before the autocomplete clears it.
         verifySelectionState("test", "ing is fun", 1, 1, false, "test", "test", true, "test");
@@ -255,7 +254,7 @@
      */
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291968")
+    @FlakyTest(message = "https://crbug.com/1291968")
     public void testSendCursorPosition() throws TimeoutException {
         final CallbackHelper autocompleteHelper = new CallbackHelper();
         final AtomicInteger cursorPositionUsed = new AtomicInteger();
@@ -399,7 +398,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291159")
+    @FlakyTest(message = "https://crbug.com/1291159")
     public void testBatchModeChangesTriggerCorrectSuggestions() {
         final AtomicReference<String> requestedAutocompleteText = new AtomicReference<String>();
         mOmnibox.setText("test");
@@ -416,7 +415,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291159")
+    @FlakyTest(message = "https://crbug.com/1291159")
     public void testAutocompleteCorrectlyPerservedOnBatchMode() {
         // Valid case (cursor at the end of text, single character, matches previous autocomplete).
         mOmnibox.setText("g");
@@ -453,7 +452,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1291159")
+    @FlakyTest(message = "https://crbug.com/1291159")
     public void testAutocompleteSpanClearedOnNonMatchingCommitText() {
         mOmnibox.setText("a");
         mOmnibox.setAutocompleteText("mazon.com");
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 39dfb67..fbf475b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5623,6 +5623,7 @@
       "chrome_browser_application_mac.mm",
       "chrome_browser_main_mac.h",
       "chrome_browser_main_mac.mm",
+      "component_updater/recovery_improved_component_installer_mac.cc",
       "download/download_status_updater_mac.mm",
       "download/drag_download_item_mac.mm",
       "enterprise/signals/device_info_fetcher_mac.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9f970127..dcb24cf 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3853,7 +3853,7 @@
     {"global-media-controls-cast-start-stop",
      flag_descriptions::kGlobalMediaControlsCastStartStopName,
      flag_descriptions::kGlobalMediaControlsCastStartStopDescription,
-     kOsWin | kOsMac | kOsLinux | kOsFuchsia,
+     kOsDesktop,
      FEATURE_VALUE_TYPE(media_router::kGlobalMediaControlsCastStartStop)},
     {"allow-all-sites-to-initiate-mirroring",
      flag_descriptions::kAllowAllSitesToInitiateMirroringName,
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.cc b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
index c436960..1a5c2e16 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.cc
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
@@ -55,12 +55,6 @@
     float search_promo_height,
     float search_promo_opacity,
     int search_promo_background_color,
-    // Panel Help
-    int panel_help_resource_id,
-    bool panel_help_visible,
-    float panel_help_height,
-    float panel_help_opacity,
-    int panel_help_container_background_color,
     // Related Searches
     int related_searches_in_content_resource_id,
     bool related_searches_in_content_visible,
@@ -122,8 +116,7 @@
       open_tab_icon_resource_id, close_icon_resource_id);
 
   //  TODO(donnd): Update when moving Related Searches.
-  float content_view_top = search_bar_bottom + panel_help_height +
-                           search_promo_height +
+  float content_view_top = search_bar_bottom + search_promo_height +
                            related_searches_in_content_height;
   float should_render_bar_border = search_bar_border_visible
       && !should_render_progress_bar;
@@ -302,42 +295,6 @@
   }
 
   // ---------------------------------------------------------------------------
-  // Panel Help
-  // ---------------------------------------------------------------------------
-  if (panel_help_visible) {
-    ui::Resource* panel_help_resource = resource_manager_->GetResource(
-        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, panel_help_resource_id);
-    if (panel_help_container_->parent() != layer_) {
-      // NOTE(donnd): This layer can appear just below the Bar so it should be
-      // always placed before the Search Bar Shadow to make sure it won't
-      // occlude the shadow.
-      layer_->InsertChild(panel_help_container_, 0);
-    }
-    if (panel_help_resource) {
-      int panel_help_content_height = panel_help_resource->size().height();
-      gfx::Size panel_help_size(search_panel_width, panel_help_height);
-      panel_help_container_->SetBounds(panel_help_size);
-      panel_help_container_->SetPosition(gfx::PointF(0.f, next_section_top));
-      panel_help_container_->SetMasksToBounds(true);
-      panel_help_container_->SetBackgroundColor(
-          panel_help_container_background_color);
-
-      if (panel_help_->parent() != panel_help_container_)
-        panel_help_container_->AddChild(panel_help_);
-
-      panel_help_->SetUIResourceId(panel_help_resource->ui_resource()->id());
-      panel_help_->SetBounds(panel_help_resource->size());
-      panel_help_->SetPosition(
-          gfx::PointF(0.f, panel_help_height - panel_help_content_height));
-      panel_help_->SetOpacity(panel_help_opacity);
-      // Next section goes beyond this section.
-      next_section_top += panel_help_height;
-    }
-  } else if (panel_help_container_.get() && panel_help_container_->parent()) {
-    panel_help_container_->RemoveFromParent();
-  }
-
-  // ---------------------------------------------------------------------------
   // Search Promo
   // ---------------------------------------------------------------------------
   if (search_promo_visible) {
@@ -734,8 +691,6 @@
       search_provider_icon_layer_(cc::UIResourceLayer::Create()),
       thumbnail_layer_(cc::UIResourceLayer::Create()),
       quick_action_icon_layer_(cc::UIResourceLayer::Create()),
-      panel_help_(cc::UIResourceLayer::Create()),
-      panel_help_container_(cc::SolidColorLayer::Create()),
       search_promo_(cc::UIResourceLayer::Create()),
       search_promo_container_(cc::SolidColorLayer::Create()),
       related_searches_in_bar_(cc::UIResourceLayer::Create()),
@@ -761,11 +716,6 @@
   // Search Bar Caption
   search_caption_->SetIsDrawable(true);
 
-  // In-Panel Help section
-  panel_help_container_->SetIsDrawable(true);
-  panel_help_container_->SetBackgroundColor(kSearchBackgroundColor);
-  panel_help_->SetIsDrawable(true);
-
   // Search Opt Out Promo
   search_promo_container_->SetIsDrawable(true);
   search_promo_container_->SetBackgroundColor(kSearchBackgroundColor);
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.h b/chrome/browser/android/compositor/layer/contextual_search_layer.h
index 833282d..fc63112c 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.h
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.h
@@ -55,12 +55,6 @@
                      float search_promo_height,
                      float search_promo_opacity,
                      int search_promo_background_color,
-                     // Panel Help
-                     int panel_help_resource_id,
-                     bool panel_help_visible,
-                     float panel_help_height,
-                     float panel_help_opacity,
-                     int panel_help_container_background_color,
                      // Related Searches
                      int related_searches_in_content_resource_id,
                      bool related_searches_in_content_visible,
@@ -152,8 +146,6 @@
   scoped_refptr<cc::UIResourceLayer> search_provider_icon_layer_;
   scoped_refptr<cc::UIResourceLayer> thumbnail_layer_;
   scoped_refptr<cc::UIResourceLayer> quick_action_icon_layer_;
-  scoped_refptr<cc::UIResourceLayer> panel_help_;
-  scoped_refptr<cc::SolidColorLayer> panel_help_container_;
   scoped_refptr<cc::UIResourceLayer> search_promo_;
   scoped_refptr<cc::SolidColorLayer> search_promo_container_;
   scoped_refptr<cc::UIResourceLayer> related_searches_in_bar_;
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
index f946336..4dd0f8c 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
@@ -93,12 +93,6 @@
     jfloat search_promo_height,
     jfloat search_promo_opacity,
     jint search_promo_background_color,
-    // Panel Help
-    jint panel_help_resource_id,
-    jboolean panel_help_visible,
-    jfloat panel_help_height,
-    jfloat panel_help_opacity,
-    jint panel_help_container_background_color,
     // Related Searches
     jint related_searches_in_content_resource_id,
     jboolean related_searches_in_content_visible,
@@ -183,9 +177,6 @@
       bar_banner_ripple_resource_id, bar_banner_text_resource_id, dp_to_px,
       content_layer, search_promo_visible, search_promo_height,
       search_promo_opacity, search_promo_background_color,
-      // Panel Help
-      panel_help_resource_id, panel_help_visible, panel_help_height,
-      panel_help_opacity, panel_help_container_background_color,
       // Related Searches
       related_searches_in_content_resource_id,
       related_searches_in_content_visible, related_searches_in_content_height,
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
index d44f560..bd0a911 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
@@ -74,12 +74,6 @@
       jfloat search_promo_height,
       jfloat search_promo_opacity,
       jint search_promo_background_color,
-      // Panel Help
-      jint panel_help_resource_id,
-      jboolean panel_help_visible,
-      jfloat panel_help_height,
-      jfloat panel_help_opacity,
-      jint panel_help_container_background_color,
       // Related Searches
       jint related_searches_in_content_resource_id,
       jboolean related_searches_in_content_visible,
diff --git a/chrome/browser/apps/app_service/menu_util.cc b/chrome/browser/apps/app_service/menu_util.cc
index ec9907d..8b2f3a4 100644
--- a/chrome/browser/apps/app_service/menu_util.cc
+++ b/chrome/browser/apps/app_service/menu_util.cc
@@ -166,13 +166,14 @@
     return false;
   }
 
+  const ui::ColorId color_id = GetColorIdForMenuItemIcon();
   switch (item->type) {
     case apps::mojom::MenuItemType::kCommand: {
       const gfx::VectorIcon& icon =
           std::move(get_vector_icon).Run(item->command_id, item->string_id);
       model->AddItemWithStringIdAndIcon(
           item->command_id, item->string_id,
-          ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+          ui::ImageModel::FromVectorIcon(icon, color_id,
                                          ash::kAppContextMenuIconSize));
       break;
     }
@@ -183,7 +184,7 @@
             std::move(get_vector_icon).Run(item->command_id, item->string_id);
         model->AddActionableSubmenuWithStringIdAndIcon(
             item->command_id, item->string_id, submenu,
-            ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+            ui::ImageModel::FromVectorIcon(icon, color_id,
                                            ash::kAppContextMenuIconSize));
       }
       break;
@@ -266,4 +267,12 @@
   return menu_items;
 }
 
+ui::ColorId GetColorIdForMenuItemIcon() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  return ui::kColorAshSystemUIMenuIcon;
+#else
+  return ui::kColorMenuIcon;
+#endif
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/menu_util.h b/chrome/browser/apps/app_service/menu_util.h
index 5456970..e063fbb 100644
--- a/chrome/browser/apps/app_service/menu_util.h
+++ b/chrome/browser/apps/app_service/menu_util.h
@@ -97,6 +97,9 @@
 // Returns the browser menu items for the given |menu_type|.
 mojom::MenuItemsPtr CreateBrowserMenuItems(mojom::MenuType menu_type,
                                            const Profile* profile);
+
+ui::ColorId GetColorIdForMenuItemIcon();
+
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_APP_SERVICE_MENU_UTIL_H_
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
index 7e2edb9..36f0425 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
@@ -96,6 +96,8 @@
 const test::UIPath kFooterLearnMorePopUpClose = {
     kConsolidatedConsentId, "footerLearnMorePopUp", "closeButton"};
 const test::UIPath kAcceptButton = {kConsolidatedConsentId, "acceptButton"};
+const test::UIPath kReadMoreButton = {kConsolidatedConsentId, "loadedDialog",
+                                      "readMoreButton"};
 
 // Google EUlA Dialog
 const test::UIPath kGoogleEulaDialog = {kConsolidatedConsentId,
@@ -273,8 +275,8 @@
   test::OobeJS().CreateVisibilityWaiter(true, kLoadedDialog)->Wait();
 
   test::OobeJS().ClickOnPath(kCrosEulaOkButton);
-  test::OobeJS().CreateVisibilityWaiter(true, kAcceptButton)->Wait();
 
+  test::OobeJS().CreateVisibilityWaiter(true, kAcceptButton)->Wait();
   test::OobeJS().ClickOnPath(kAcceptButton);
   WaitForScreenExit();
   EXPECT_EQ(screen_result_.value(),
@@ -446,7 +448,7 @@
         RecordArcGoogleLocationServiceConsent(
             testing::_, consent_auditor::ArcGoogleLocationServiceConsentEq(
                             location_service_consent)));
-
+    test::OobeJS().CreateVisibilityWaiter(true, kAcceptButton)->Wait();
     test::OobeJS().ClickOnPath(kAcceptButton);
   }
 
@@ -609,4 +611,28 @@
             ConsolidatedConsentScreen::Result::NOT_APPLICABLE);
 }
 
+// Show the screen in a low resolution to guarantee that the `Read More` button
+// is shown.
+class ConsolidatedConsentScreenReadMore
+    : public ConsolidatedConsentScreenArcEnabledTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Use low resolution screen to force "Read More" button to be shown
+    command_line->AppendSwitchASCII("ash-host-window-bounds", "900x600");
+    ConsolidatedConsentScreenArcEnabledTest::SetUpCommandLine(command_line);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ConsolidatedConsentScreenReadMore, ClickAccept) {
+  LoginAsRegularUser();
+  OobeScreenWaiter(ConsolidatedConsentScreenView::kScreenId).Wait();
+  test::OobeJS().CreateVisibilityWaiter(true, kLoadedDialog)->Wait();
+  test::OobeJS()
+      .CreateWaiter(test::GetOobeElementPath(kReadMoreButton) + " != null")
+      ->Wait();
+  test::OobeJS().ClickOnPath(kReadMoreButton);
+  test::OobeJS().CreateVisibilityWaiter(true, kAcceptButton)->Wait();
+  test::OobeJS().ClickOnPath(kAcceptButton);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/login/test/js_checker.cc b/chrome/browser/ash/login/test/js_checker.cc
index 7cd851b..f5fbf27 100644
--- a/chrome/browser/ash/login/test/js_checker.cc
+++ b/chrome/browser/ash/login/test/js_checker.cc
@@ -531,6 +531,12 @@
   Evaluate(js);
 }
 
+bool JSChecker::IsVisible(
+    std::initializer_list<base::StringPiece> element_ids) {
+  bool is_hidden = GetBool(test::GetOobeElementPath(element_ids) + ".hidden");
+  return !is_hidden;
+}
+
 JSChecker OobeJS() {
   return JSChecker(LoginDisplayHost::default_host()->GetOobeWebContents());
 }
diff --git a/chrome/browser/ash/login/test/js_checker.h b/chrome/browser/ash/login/test/js_checker.h
index 04af8ed..ea580a3 100644
--- a/chrome/browser/ash/login/test/js_checker.h
+++ b/chrome/browser/ash/login/test/js_checker.h
@@ -228,6 +228,8 @@
       const std::string& value,
       std::initializer_list<base::StringPiece> element_ids);
 
+  bool IsVisible(std::initializer_list<base::StringPiece> element_ids);
+
   void set_web_contents(content::WebContents* web_contents) {
     web_contents_ = web_contents;
   }
diff --git a/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc b/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
index bbd53c3..7268858 100644
--- a/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
+++ b/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
@@ -57,8 +57,7 @@
           rollback_network_config::OncIsEapWithoutClientCertificate(network));
 }
 
-void PrintError(const std::string& error_name,
-                std::unique_ptr<base::DictionaryValue>) {
+void PrintError(const std::string& error_name) {
   LOG(ERROR) << error_name;
 }
 
@@ -91,14 +90,13 @@
                        onc::network_config::kSourceDevice);
   if (!network_state || !network_state->IsInProfile()) {
     managed_network_configuration_handler()->CreateConfiguration(
-        kDeviceUserHash, base::Value::AsDictionaryValue(network),
+        kDeviceUserHash, network,
         base::BindOnce([](const std::string&, const std::string&) {
         }).Then(std::move(success_callback)),
         base::BindOnce(&PrintError).Then(std::move(failure_callback)));
   } else if (network_state) {
     managed_network_configuration_handler()->SetProperties(
-        network_state->path(), base::Value::AsDictionaryValue(network),
-        std::move(success_callback),
+        network_state->path(), network, std::move(success_callback),
         base::BindOnce(&PrintError).Then(std::move(failure_callback)));
   }
 }
@@ -132,8 +130,8 @@
   rollback_network_config::ManagedOncCollapseToUiData(&ui_data);
 
   managed_network_configuration_handler()->SetProperties(
-      network_state->path(), base::Value::AsDictionaryValue(ui_data),
-      base::DoNothing(), base::BindOnce(&PrintError));
+      network_state->path(), ui_data, base::DoNothing(),
+      base::BindOnce(&PrintError));
 }
 
 using GetPasswordResult =
diff --git a/chrome/browser/ash/net/rollback_network_config/rollback_network_config_unittest.cc b/chrome/browser/ash/net/rollback_network_config/rollback_network_config_unittest.cc
index da80d90..09d963b 100644
--- a/chrome/browser/ash/net/rollback_network_config/rollback_network_config_unittest.cc
+++ b/chrome/browser/ash/net/rollback_network_config/rollback_network_config_unittest.cc
@@ -151,8 +151,7 @@
   return local_state;
 }
 
-void PrintErrorAndFail(const std::string& error_name,
-                       std::unique_ptr<base::DictionaryValue>) {
+void PrintErrorAndFail(const std::string& error_name) {
   LOG(ERROR) << error_name;
   FAIL();
 }
@@ -194,7 +193,7 @@
 void SetUpDeviceWideNetworkConfig(const base::Value& config) {
   base::RunLoop run_loop;
   managed_network_configuration_handler()->CreateConfiguration(
-      kDeviceUserHash, base::Value::AsDictionaryValue(config),
+      kDeviceUserHash, config,
       base::BindLambdaForTesting(
           [&](const std::string& service_path, const std::string& guid) {
             run_loop.Quit();
@@ -210,7 +209,7 @@
   const chromeos::NetworkState* network_state =
       network_state_handler()->GetNetworkStateFromGuid(guid);
   managed_network_configuration_handler()->SetProperties(
-      network_state->path(), base::Value::AsDictionaryValue(config),
+      network_state->path(), config,
       base::BindLambdaForTesting([&]() { run_loop.Quit(); }),
       base::BindOnce(&PrintErrorAndFail));
   run_loop.Run();
diff --git a/chrome/browser/ash/policy/networking/euicc_status_uploader.cc b/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
index 40ff38e..da9073a6 100644
--- a/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
+++ b/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
@@ -68,25 +68,20 @@
     LOG(WARNING) << "NetworkHandler is not initialized.";
     return;
   }
-  chromeos::NetworkHandler::Get()
-      ->managed_network_configuration_handler()
-      ->AddObserver(this);
+  network_handler_ = chromeos::NetworkHandler::Get();
+  network_handler_->managed_network_configuration_handler()->AddObserver(this);
   chromeos::HermesEuiccClient::Get()->AddObserver(this);
-  chromeos::NetworkHandler::Get()->network_state_handler()->AddObserver(
-      this, FROM_HERE);
+  network_handler_->network_state_handler()->AddObserver(this, FROM_HERE);
+
   MaybeUploadStatus();
 }
 
 EuiccStatusUploader::~EuiccStatusUploader() {
-  if (chromeos::NetworkHandler::IsInitialized()) {
-    chromeos::NetworkHandler::Get()
-        ->managed_network_configuration_handler()
-        ->RemoveObserver(this);
-    chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
-        this, FROM_HERE);
-  }
   if (chromeos::HermesEuiccClient::Get())
     chromeos::HermesEuiccClient::Get()->RemoveObserver(this);
+
+  if (network_handler_)
+    OnShuttingDown();
 }
 
 // static
@@ -122,6 +117,13 @@
   return upload_request;
 }
 
+void EuiccStatusUploader::OnShuttingDown() {
+  network_handler_->network_state_handler()->RemoveObserver(this, FROM_HERE);
+  network_handler_->managed_network_configuration_handler()->RemoveObserver(
+      this);
+  network_handler_ = nullptr;
+}
+
 void EuiccStatusUploader::PoliciesApplied(const std::string& userhash) {
   MaybeUploadStatus();
 }
@@ -148,11 +150,10 @@
   base::Value esim_profiles(base::Value::Type::LIST);
 
   chromeos::NetworkStateHandler::NetworkStateList networks;
-  chromeos::NetworkHandler::Get()
-      ->network_state_handler()
-      ->GetNetworkListByType(ash::NetworkTypePattern::Cellular(),
-                             /*configure_only=*/false, /*visible_only=*/false,
-                             /*limit=*/0, &networks);
+  network_handler_->network_state_handler()->GetNetworkListByType(
+      ash::NetworkTypePattern::Cellular(),
+      /*configure_only=*/false, /*visible_only=*/false,
+      /*limit=*/0, &networks);
 
   onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
   for (const chromeos::NetworkState* network : networks) {
@@ -166,10 +167,9 @@
 
     // Read the SMDP address from ONC.
     const base::Value* policy =
-        chromeos::NetworkHandler::Get()
-            ->managed_network_configuration_handler()
-            ->FindPolicyByGUID(/*userhash=*/std::string(), network->guid(),
-                               &onc_source);
+        network_handler_->managed_network_configuration_handler()
+            ->FindPolicyByGUID(
+                /*userhash=*/std::string(), network->guid(), &onc_source);
     DCHECK(policy);
 
     const base::Value* cellular_dict =
@@ -195,6 +195,10 @@
 }
 
 void EuiccStatusUploader::MaybeUploadStatus() {
+  if (!network_handler_) {
+    LOG(WARNING) << "NetworkHandler is not initialized.";
+    return;
+  }
   const base::Value* last_uploaded_pref =
       local_state_->Get(kLastUploadedEuiccStatusPref);
   auto current_state = GetCurrentEuiccStatus();
diff --git a/chrome/browser/ash/policy/networking/euicc_status_uploader.h b/chrome/browser/ash/policy/networking/euicc_status_uploader.h
index 3b5c7b6..c537fea 100644
--- a/chrome/browser/ash/policy/networking/euicc_status_uploader.h
+++ b/chrome/browser/ash/policy/networking/euicc_status_uploader.h
@@ -68,6 +68,7 @@
 
   // chromeos::NetworkStateHandlerObserver:
   void NetworkListChanged() override;
+  void OnShuttingDown() override;
 
   // chromeos::HermesEuiccClient:
   void OnEuiccReset(const dbus::ObjectPath& euicc_path) override;
@@ -93,6 +94,8 @@
   std::unique_ptr<base::OneShotTimer> retry_timer_;
   net::BackoffEntry retry_entry_;
 
+  chromeos::NetworkHandler* network_handler_ = nullptr;
+
   base::WeakPtrFactory<EuiccStatusUploader> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/policy/networking/euicc_status_uploader_unittest.cc b/chrome/browser/ash/policy/networking/euicc_status_uploader_unittest.cc
index 5809d48..bfc0deab 100644
--- a/chrome/browser/ash/policy/networking/euicc_status_uploader_unittest.cc
+++ b/chrome/browser/ash/policy/networking/euicc_status_uploader_unittest.cc
@@ -396,4 +396,28 @@
                          /*clear_profile_list=*/true);
 }
 
+TEST_F(EuiccStatusUploaderTest, UnexpectedNetworkHandlerShutdown) {
+  SetUpDeviceProfiles(kSetupOneEsimProfile);
+  // NetworkHandler has not been initialized.
+  auto status_uploader = CreateStatusUploader();
+
+  // Initial Request
+  EXPECT_EQ(GetRequestCount(), 1);
+
+  // Requests made normally.
+  UpdateUploader(status_uploader.get());
+  EXPECT_EQ(GetRequestCount(), 2);
+
+  // NetworkHandler::Shutdown() has already been called before
+  // EuiccStatusUploader is deleted
+  chromeos::NetworkHandler::Shutdown();
+
+  // No requests made as NetworkHandler is not available.
+  UpdateUploader(status_uploader.get());
+  EXPECT_EQ(GetRequestCount(), 2);
+
+  // Need to reinitialize before exiting test.
+  chromeos::NetworkHandler::Initialize();
+}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/system_logs/command_line_log_source.cc b/chrome/browser/ash/system_logs/command_line_log_source.cc
index eba1b814..a1b98ef 100644
--- a/chrome/browser/ash/system_logs/command_line_log_source.cc
+++ b/chrome/browser/ash/system_logs/command_line_log_source.cc
@@ -63,6 +63,7 @@
 
   // Get disk space usage information
   command = base::CommandLine(base::FilePath("/bin/df"));
+  command.AppendArg("--human-readable");
   commands.emplace_back("disk_usage", command);
 
   for (const auto& command : commands) {
diff --git a/chrome/browser/ash/web_applications/DEPS b/chrome/browser/ash/web_applications/DEPS
index 40dda84d..b37f0f8 100644
--- a/chrome/browser/ash/web_applications/DEPS
+++ b/chrome/browser/ash/web_applications/DEPS
@@ -3,6 +3,7 @@
     "+chrome/browser/ui/views",
   ],
   "personalization_app_integration_browsertest.cc": [
+    "+ash/ambient/test/ambient_ash_test_helper.h",
     "+chrome/browser/ui/views/frame/browser_view.h",
     "+ui/message_center/message_center.h",
   ],
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc
index a77a4b6..b29ddca 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.cc
@@ -4,14 +4,66 @@
 
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h"
 
+#include <algorithm>
+#include <string>
 #include <utility>
+#include <vector>
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/ambient/ambient_client.h"
 #include "ash/public/cpp/ambient/ambient_prefs.h"
+#include "ash/public/cpp/ambient/common/ambient_settings.h"
+#include "ash/public/cpp/image_downloader.h"
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/notreached.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_service.h"
+#include "net/base/backoff_entry.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace {
+
+// Width and height of the preview image for personal album.
+constexpr int kBannerWidthPx = 160;
+constexpr int kBannerHeightPx = 160;
+
+constexpr int kMaxRetries = 3;
+
+constexpr net::BackoffEntry::Policy kRetryBackoffPolicy = {
+    0,          // Number of initial errors to ignore.
+    500,        // Initial delay in ms.
+    2.0,        // Factor by which the waiting time will be multiplied.
+    0.2,        // Fuzzing percentage.
+    60 * 1000,  // Maximum delay in ms.
+    -1,         // Never discard the entry.
+    true,       // Use initial delay.
+};
+
+void EncodeImage(const gfx::ImageSkia& image,
+                 std::vector<unsigned char>* output) {
+  if (!gfx::PNGCodec::EncodeBGRASkBitmap(*image.bitmap(),
+                                         /*discard_transparency=*/false,
+                                         output)) {
+    VLOG(1) << "Failed to encode image to png";
+    output->clear();
+  }
+}
+
+}  // namespace
 
 PersonalizationAppAmbientProviderImpl::PersonalizationAppAmbientProviderImpl(
     content::WebUI* web_ui)
-    : profile_(Profile::FromWebUI(web_ui)) {}
+    : profile_(Profile::FromWebUI(web_ui)),
+      fetch_settings_retry_backoff_(&kRetryBackoffPolicy),
+      update_settings_retry_backoff_(&kRetryBackoffPolicy) {}
 
 PersonalizationAppAmbientProviderImpl::
     ~PersonalizationAppAmbientProviderImpl() = default;
@@ -42,6 +94,14 @@
   PrefService* pref_service = profile_->GetPrefs();
   OnAmbientModeEnabledChanged(
       pref_service->GetBoolean(ash::ambient::prefs::kAmbientModeEnabled));
+
+  write_weak_factory_.InvalidateWeakPtrs();
+  read_weak_factory_.InvalidateWeakPtrs();
+  album_preview_weak_factory_.InvalidateWeakPtrs();
+  recent_highlights_previews_weak_factory_.InvalidateWeakPtrs();
+
+  // Will notify WebUI when fetches successfully.
+  FetchSettingsAndAlbums();
 }
 
 void PersonalizationAppAmbientProviderImpl::SetAmbientModeEnabled(
@@ -56,3 +116,333 @@
   DCHECK(ambient_observer_remote_.is_bound());
   ambient_observer_remote_->OnAmbientModeEnabledChanged(ambient_mode_enabled);
 }
+
+void PersonalizationAppAmbientProviderImpl::OnTemperatureUnitChanged() {
+  NOTIMPLEMENTED();
+}
+
+void PersonalizationAppAmbientProviderImpl::OnTopicSourceChanged() {
+  NOTIMPLEMENTED();
+}
+
+void PersonalizationAppAmbientProviderImpl::OnAlbumsChanged() {
+  NOTIMPLEMENTED();
+}
+
+void PersonalizationAppAmbientProviderImpl::OnAlbumPreviewChanged(
+    const std::string& album_id,
+    std::string&& png_data_url) {
+  NOTIMPLEMENTED();
+}
+
+void PersonalizationAppAmbientProviderImpl::
+    OnRecentHighlightsPreviewsChanged() {
+  NOTIMPLEMENTED();
+}
+
+bool PersonalizationAppAmbientProviderImpl::IsAmbientModeEnabled() {
+  PrefService* pref_service = profile_->GetPrefs();
+  DCHECK(pref_service);
+  return pref_service->GetBoolean(ash::ambient::prefs::kAmbientModeEnabled);
+}
+
+void PersonalizationAppAmbientProviderImpl::UpdateSettings() {
+  DCHECK(IsAmbientModeEnabled())
+      << "Ambient mode must be enabled to update settings";
+  DCHECK(settings_);
+
+  // Prevent fetch settings callback changing `settings_` and `personal_albums_`
+  // while updating.
+  read_weak_factory_.InvalidateWeakPtrs();
+
+  if (is_updating_backend_) {
+    has_pending_updates_for_backend_ = true;
+    return;
+  }
+
+  has_pending_updates_for_backend_ = false;
+  is_updating_backend_ = true;
+
+  // Explicitly set show_weather to true to force server to respond with
+  // weather information. See: b/158630188.
+  settings_->show_weather = true;
+
+  settings_sent_for_update_ = settings_;
+  ash::AmbientBackendController::Get()->UpdateSettings(
+      *settings_,
+      base::BindOnce(&PersonalizationAppAmbientProviderImpl::OnUpdateSettings,
+                     write_weak_factory_.GetWeakPtr()));
+}
+
+void PersonalizationAppAmbientProviderImpl::OnUpdateSettings(bool success) {
+  is_updating_backend_ = false;
+
+  if (success) {
+    update_settings_retry_backoff_.Reset();
+    cached_settings_ = settings_sent_for_update_;
+  } else {
+    update_settings_retry_backoff_.InformOfRequest(/*succeeded=*/false);
+  }
+
+  if (MaybeScheduleNewUpdateSettings(success))
+    return;
+
+  UpdateUIWithCachedSettings(success);
+}
+
+bool PersonalizationAppAmbientProviderImpl::MaybeScheduleNewUpdateSettings(
+    bool success) {
+  // If it was unsuccessful to update settings, but have not reached
+  // `kMaxRetries`, then it will retry.
+  const bool need_retry_update_settings_at_backend =
+      !success && update_settings_retry_backoff_.failure_count() <= kMaxRetries;
+
+  // If there has pending updates or need to retry, then updates settings again.
+  const bool should_update_settings_at_backend =
+      has_pending_updates_for_backend_ || need_retry_update_settings_at_backend;
+
+  if (!should_update_settings_at_backend)
+    return false;
+
+  const base::TimeDelta kDelay =
+      update_settings_retry_backoff_.GetTimeUntilRelease();
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&PersonalizationAppAmbientProviderImpl::UpdateSettings,
+                     write_weak_factory_.GetWeakPtr()),
+      kDelay);
+  return true;
+}
+
+void PersonalizationAppAmbientProviderImpl::UpdateUIWithCachedSettings(
+    bool success) {
+  // If it was unsuccessful to update settings with `kMaxRetries`, need to
+  // restore to cached settings.
+  const bool should_restore_previous_settings =
+      !success && update_settings_retry_backoff_.failure_count() > kMaxRetries;
+
+  // Otherwise, if there has pending fetching request or need to restore
+  // cached settings, then updates the WebUi.
+  const bool should_update_web_ui =
+      has_pending_fetch_request_ || should_restore_previous_settings;
+
+  if (!should_update_web_ui)
+    return;
+
+  OnSettingsAndAlbumsFetched(cached_settings_, std::move(personal_albums_));
+  has_pending_fetch_request_ = false;
+}
+
+void PersonalizationAppAmbientProviderImpl::FetchSettingsAndAlbums() {
+  // If there is an ongoing update, do not fetch. If update succeeds, it will
+  // update the UI with the new settings. If update fails, it will restore
+  // previous settings and update UI.
+  if (is_updating_backend_) {
+    has_pending_fetch_request_ = true;
+    return;
+  }
+
+  // TODO(b/161044021): Add a helper function to get all the albums. Currently
+  // only load 100 latest modified albums.
+  ash::AmbientBackendController::Get()->FetchSettingsAndAlbums(
+      kBannerWidthPx, kBannerHeightPx, /*num_albums=*/100,
+      base::BindOnce(
+          &PersonalizationAppAmbientProviderImpl::OnSettingsAndAlbumsFetched,
+          read_weak_factory_.GetWeakPtr()));
+}
+
+void PersonalizationAppAmbientProviderImpl::OnSettingsAndAlbumsFetched(
+    const absl::optional<ash::AmbientSettings>& settings,
+    ash::PersonalAlbums personal_albums) {
+  // `settings` value implies success.
+  if (!settings) {
+    fetch_settings_retry_backoff_.InformOfRequest(/*succeeded=*/false);
+    if (fetch_settings_retry_backoff_.failure_count() > kMaxRetries)
+      return;
+
+    const base::TimeDelta kDelay =
+        fetch_settings_retry_backoff_.GetTimeUntilRelease();
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(
+            &PersonalizationAppAmbientProviderImpl::FetchSettingsAndAlbums,
+            read_weak_factory_.GetWeakPtr()),
+        kDelay);
+    return;
+  }
+
+  fetch_settings_retry_backoff_.Reset();
+  settings_ = settings;
+  cached_settings_ = settings;
+  personal_albums_ = std::move(personal_albums);
+  SyncSettingsAndAlbums();
+
+  // Will notify WebUI when downloads successfully.
+  DownloadAlbumPreviewImage();
+
+  OnTemperatureUnitChanged();
+  OnTopicSourceChanged();
+  OnAlbumsChanged();
+
+  // If weather info is disabled, call `UpdateSettings()` immediately to force
+  // it to true.
+  if (!settings_->show_weather && IsAmbientModeEnabled()) {
+    UpdateSettings();
+  }
+}
+
+void PersonalizationAppAmbientProviderImpl::SyncSettingsAndAlbums() {
+  auto it = settings_->selected_album_ids.begin();
+  while (it != settings_->selected_album_ids.end()) {
+    const std::string& album_id = *it;
+    ash::PersonalAlbum* album = FindPersonalAlbumById(album_id);
+    if (album) {
+      album->selected = true;
+      ++it;
+    } else {
+      // The selected album does not exist any more.
+      it = settings_->selected_album_ids.erase(it);
+    }
+  }
+
+  if (settings_->selected_album_ids.empty())
+    MaybeUpdateTopicSource(ash::AmbientModeTopicSource::kArtGallery);
+}
+
+void PersonalizationAppAmbientProviderImpl::UpdateTopicSource(
+    ash::AmbientModeTopicSource topic_source) {
+  // If this is an Art gallery album page, will select art gallery topic source.
+  if (topic_source == ash::AmbientModeTopicSource::kArtGallery) {
+    MaybeUpdateTopicSource(topic_source);
+    return;
+  }
+
+  // If this is a Google Photos album page, will
+  // 1. Select art gallery topic source if no albums or no album is selected.
+  if (settings_->selected_album_ids.empty()) {
+    MaybeUpdateTopicSource(ash::AmbientModeTopicSource::kArtGallery);
+    return;
+  }
+
+  // 2. Select Google Photos topic source if at least one album is selected.
+  MaybeUpdateTopicSource(ash::AmbientModeTopicSource::kGooglePhotos);
+}
+
+void PersonalizationAppAmbientProviderImpl::MaybeUpdateTopicSource(
+    ash::AmbientModeTopicSource topic_source) {
+  // If the setting is the same, no need to update.
+  if (settings_->topic_source == topic_source)
+    return;
+
+  settings_->topic_source = topic_source;
+  UpdateSettings();
+  OnTopicSourceChanged();
+}
+
+void PersonalizationAppAmbientProviderImpl::DownloadAlbumPreviewImage() {
+  // Art gallery:
+  for (const auto& album : settings_->art_settings) {
+    ash::AmbientClient::Get()->DownloadImage(
+        album.preview_image_url,
+        base::BindOnce(&PersonalizationAppAmbientProviderImpl::
+                           OnAlbumPreviewImageDownloaded,
+                       album_preview_weak_factory_.GetWeakPtr(),
+                       album.album_id));
+  }
+
+  // GooglePhotos:
+  // TODO(b/163413738): Slow down the downloading when there are too many
+  // albums.
+  for (const auto& album : personal_albums_.albums) {
+    if (album.album_id == ash::kAmbientModeRecentHighlightsAlbumId) {
+      DownloadRecentHighlightsPreviewImages(album.preview_image_urls);
+      continue;
+    }
+
+    ash::AmbientClient::Get()->DownloadImage(
+        album.banner_image_url,
+        base::BindOnce(&PersonalizationAppAmbientProviderImpl::
+                           OnAlbumPreviewImageDownloaded,
+                       album_preview_weak_factory_.GetWeakPtr(),
+                       album.album_id));
+  }
+}
+
+void PersonalizationAppAmbientProviderImpl::OnAlbumPreviewImageDownloaded(
+    const std::string& album_id,
+    const gfx::ImageSkia& image) {
+  // Album does not exist any more.
+  if (!FindArtAlbumById(album_id) && !FindPersonalAlbumById(album_id)) {
+    return;
+  }
+
+  std::vector<unsigned char> encoded_image_bytes;
+  EncodeImage(image, &encoded_image_bytes);
+  if (encoded_image_bytes.empty())
+    return;
+
+  OnAlbumPreviewChanged(album_id,
+                        webui::GetPngDataUrl(&encoded_image_bytes.front(),
+                                             encoded_image_bytes.size()));
+}
+
+void PersonalizationAppAmbientProviderImpl::
+    DownloadRecentHighlightsPreviewImages(
+        const std::vector<std::string>& urls) {
+  recent_highlights_previews_weak_factory_.InvalidateWeakPtrs();
+
+  // Only show up to 4 previews.
+  constexpr int kMaxRecentHighlightsPreviews = 4;
+  const int total_previews =
+      std::min(kMaxRecentHighlightsPreviews, static_cast<int>(urls.size()));
+  recent_highlights_preview_images_.resize(total_previews);
+  auto on_done = base::BarrierClosure(
+      total_previews,
+      base::BindOnce(&PersonalizationAppAmbientProviderImpl::
+                         OnRecentHighlightsPreviewsChanged,
+                     recent_highlights_previews_weak_factory_.GetWeakPtr()));
+
+  for (int url_index = 0; url_index < total_previews; ++url_index) {
+    const auto& url = urls[url_index];
+    ash::AmbientClient::Get()->DownloadImage(
+        url,
+        base::BindOnce(
+            [](std::vector<gfx::ImageSkia>* preview_images, int url_index,
+               base::RepeatingClosure on_done,
+               base::WeakPtr<PersonalizationAppAmbientProviderImpl> weak_ptr,
+               const gfx::ImageSkia& image) {
+              if (!weak_ptr)
+                return;
+
+              (*preview_images)[url_index] = image;
+              on_done.Run();
+            },
+            &recent_highlights_preview_images_, url_index, on_done,
+            recent_highlights_previews_weak_factory_.GetWeakPtr()));
+  }
+}
+
+ash::PersonalAlbum*
+PersonalizationAppAmbientProviderImpl::FindPersonalAlbumById(
+    const std::string& album_id) {
+  auto it = std::find_if(
+      personal_albums_.albums.begin(), personal_albums_.albums.end(),
+      [&album_id](const auto& album) { return album.album_id == album_id; });
+
+  if (it == personal_albums_.albums.end())
+    return nullptr;
+
+  return &(*it);
+}
+
+ash::ArtSetting* PersonalizationAppAmbientProviderImpl::FindArtAlbumById(
+    const std::string& album_id) {
+  auto it = std::find_if(
+      settings_->art_settings.begin(), settings_->art_settings.end(),
+      [&album_id](const auto& album) { return album.album_id == album_id; });
+  // Album does not exist any more.
+  if (it == settings_->art_settings.end())
+    return nullptr;
+
+  return &(*it);
+}
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h
index 5a61c895..c06e79a 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h
@@ -5,13 +5,25 @@
 #ifndef CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_AMBIENT_PROVIDER_IMPL_H_
 #define CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_AMBIENT_PROVIDER_IMPL_H_
 
+#include "ash/public/cpp/ambient/ambient_backend_controller.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "ash/webui/personalization_app/personalization_app_ambient_provider.h"
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/web_ui.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/backoff_entry.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+struct AmbientSettings;
+}  // namespace ash
+
+namespace gfx {
+class ImageSkia;
+}  // namespace gfx
 
 class Profile;
 
@@ -41,7 +53,59 @@
   // TODO(b/216307771): Will need to add observer for this.
   void OnAmbientModeEnabledChanged(bool ambient_mode_enabled);
 
+  // Notify WebUI the latest values.
+  void OnTemperatureUnitChanged();
+  void OnTopicSourceChanged();
+  void OnAlbumsChanged();
+  void OnAlbumPreviewChanged(const std::string& album_id,
+                             std::string&& png_data_url);
+  void OnRecentHighlightsPreviewsChanged();
+
  private:
+  friend class PersonalizationAppAmbientProviderImplTest;
+
+  bool IsAmbientModeEnabled();
+
+  // Update the local `settings_` to server.
+  void UpdateSettings();
+
+  // Called when the settings is updated.
+  // `success` is true when update successfully.
+  void OnUpdateSettings(bool success);
+
+  // Return true if a new update needed.
+  // `success` is true when update successfully.
+  bool MaybeScheduleNewUpdateSettings(bool success);
+
+  // `success` is true when update successfully.
+  void UpdateUIWithCachedSettings(bool success);
+
+  // Fetch settings and albums from Backdrop server.
+  void FetchSettingsAndAlbums();
+  void OnSettingsAndAlbumsFetched(
+      const absl::optional<ash::AmbientSettings>& settings,
+      ash::PersonalAlbums personal_albums);
+
+  // The `settings_` could be stale when the albums in Google Photos changes.
+  // Prune the `selected_album_id` which does not exist any more.
+  // Populate albums with selected info which will be shown on Settings UI.
+  void SyncSettingsAndAlbums();
+
+  // Update topic source if needed.
+  void UpdateTopicSource(ash::AmbientModeTopicSource topic_source);
+  void MaybeUpdateTopicSource(ash::AmbientModeTopicSource topic_source);
+
+  void DownloadAlbumPreviewImage();
+  void DownloadRecentHighlightsPreviewImages(
+      const std::vector<std::string>& urls);
+
+  void OnAlbumPreviewImageDownloaded(const std::string& album_id,
+                                     const gfx::ImageSkia& image);
+
+  ash::PersonalAlbum* FindPersonalAlbumById(const std::string& album_id);
+
+  ash::ArtSetting* FindArtAlbumById(const std::string& album_id);
+
   mojo::Receiver<ash::personalization_app::mojom::AmbientProvider>
       ambient_receiver_{this};
 
@@ -49,6 +113,47 @@
       ambient_observer_remote_;
 
   raw_ptr<Profile> const profile_ = nullptr;
+
+  // Local settings which may contain changes from WebUI but have not sent to
+  // server. Only one `UpdateSettgings()` at a time.
+  absl::optional<ash::AmbientSettings> settings_;
+
+  // The cached settings from the server. Should be the same as the server side.
+  // This value will be updated when `RequestSettingsAndAlbums()` and
+  // `UpdateSettings()` return successfully.
+  // If `UpdateSettings()` fails, will restore to this value.
+  absl::optional<ash::AmbientSettings> cached_settings_;
+
+  // A temporary settings sent to the server in `UpdateSettings()`.
+  absl::optional<ash::AmbientSettings> settings_sent_for_update_;
+
+  ash::PersonalAlbums personal_albums_;
+
+  // Backoff retries for `FetchSettingsAndAlbums()`.
+  net::BackoffEntry fetch_settings_retry_backoff_;
+
+  // Whether to update UI when `UpdateSettings()` returns successfully.
+  bool has_pending_fetch_request_ = false;
+
+  // Whether the Settings updating is ongoing.
+  bool is_updating_backend_ = false;
+
+  // Whether there are pending updates.
+  bool has_pending_updates_for_backend_ = false;
+
+  // Backoff retries for `UpdateSettings()`.
+  net::BackoffEntry update_settings_retry_backoff_;
+
+  std::vector<gfx::ImageSkia> recent_highlights_preview_images_;
+
+  base::WeakPtrFactory<PersonalizationAppAmbientProviderImpl>
+      write_weak_factory_{this};
+  base::WeakPtrFactory<PersonalizationAppAmbientProviderImpl>
+      read_weak_factory_{this};
+  base::WeakPtrFactory<PersonalizationAppAmbientProviderImpl>
+      album_preview_weak_factory_{this};
+  base::WeakPtrFactory<PersonalizationAppAmbientProviderImpl>
+      recent_highlights_previews_weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_AMBIENT_PROVIDER_IMPL_H_
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
index 1cbfd30..e93f8621 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc
@@ -6,11 +6,15 @@
 
 #include <memory>
 
+#include "ash/ambient/test/ambient_ash_test_helper.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/ambient/ambient_prefs.h"
+#include "ash/public/cpp/ambient/common/ambient_settings.h"
+#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "base/callback_helpers.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -55,6 +59,12 @@
 
     ambient_provider_->BindInterface(
         ambient_provider_remote_.BindNewPipeAndPassReceiver());
+
+    SetEnabledPref(true);
+    fake_backend_controller_ =
+        std::make_unique<ash::FakeAmbientBackendControllerImpl>();
+    ambient_ash_test_helper_ = std::make_unique<ash::AmbientAshTestHelper>();
+    ambient_ash_test_helper_->ambient_client().SetAutomaticalyIssueToken(true);
   }
 
   TestingProfile* profile() { return profile_; }
@@ -64,8 +74,74 @@
     return ambient_provider_remote_;
   }
 
+  content::TestWebUI* web_ui() { return &web_ui_; }
+
+  absl::optional<ash::AmbientSettings>& settings() {
+    return ambient_provider_->settings_;
+  }
+
+  void SetEnabledPref(bool enabled) {
+    profile()->GetPrefs()->SetBoolean(ash::ambient::prefs::kAmbientModeEnabled,
+                                      enabled);
+  }
+
+  void FetchSettings() { ambient_provider_->FetchSettingsAndAlbums(); }
+
+  void UpdateSettings() {
+    if (!ambient_provider_->settings_)
+      ambient_provider_->settings_ = ash::AmbientSettings();
+
+    ambient_provider_->UpdateSettings();
+  }
+
+  bool HasPendingFetchRequestAtProvider() const {
+    return ambient_provider_->has_pending_fetch_request_;
+  }
+
+  bool IsUpdateSettingsPendingAtProvider() const {
+    return ambient_provider_->is_updating_backend_;
+  }
+
+  bool HasPendingUpdatesAtProvider() const {
+    return ambient_provider_->has_pending_updates_for_backend_;
+  }
+
+  base::TimeDelta GetFetchSettingsDelay() {
+    return ambient_provider_->fetch_settings_retry_backoff_
+        .GetTimeUntilRelease();
+  }
+
+  base::TimeDelta GetUpdateSettingsDelay() {
+    return ambient_provider_->update_settings_retry_backoff_
+        .GetTimeUntilRelease();
+  }
+
+  void FastForwardBy(base::TimeDelta time) {
+    task_environment_.FastForwardBy(time);
+  }
+
+  bool IsFetchSettingsPendingAtBackend() const {
+    return fake_backend_controller_->IsFetchSettingsAndAlbumsPending();
+  }
+
+  void ReplyFetchSettingsAndAlbums(
+      bool success,
+      absl::optional<ash::AmbientSettings> settings = absl::nullopt) {
+    fake_backend_controller_->ReplyFetchSettingsAndAlbums(success,
+                                                          std::move(settings));
+  }
+
+  bool IsUpdateSettingsPendingAtBackend() const {
+    return fake_backend_controller_->IsUpdateSettingsPending();
+  }
+
+  void ReplyUpdateSettings(bool success) {
+    fake_backend_controller_->ReplyUpdateSettings(success);
+  }
+
  private:
-  content::BrowserTaskEnvironment task_environment_;
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   TestingProfileManager profile_manager_;
   content::TestWebUI web_ui_;
   std::unique_ptr<content::WebContents> web_contents_;
@@ -74,6 +150,10 @@
       ambient_provider_remote_;
   std::unique_ptr<PersonalizationAppAmbientProviderImpl> ambient_provider_;
   base::test::ScopedFeatureList scoped_feature_list_;
+
+  std::unique_ptr<ash::AmbientAshTestHelper> ambient_ash_test_helper_;
+  std::unique_ptr<ash::FakeAmbientBackendControllerImpl>
+      fake_backend_controller_;
 };
 
 TEST_F(PersonalizationAppAmbientProviderImplTest, IsAmbientModeEnabled) {
@@ -116,3 +196,250 @@
   EXPECT_FALSE(
       pref_service->GetBoolean(ash::ambient::prefs::kAmbientModeEnabled));
 }
+
+TEST_F(PersonalizationAppAmbientProviderImplTest, TestFetchSettings) {
+  FetchSettings();
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/true);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestFetchSettingsFailedWillRetry) {
+  FetchSettings();
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  FastForwardBy(GetFetchSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestFetchSettingsSecondRetryWillBackoff) {
+  FetchSettings();
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  base::TimeDelta delay1 = GetFetchSettingsDelay();
+  FastForwardBy(delay1 * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  base::TimeDelta delay2 = GetFetchSettingsDelay();
+  EXPECT_GT(delay2, delay1);
+
+  FastForwardBy(delay2 * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestFetchSettingsWillNotRetryMoreThanThreeTimes) {
+  FetchSettings();
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  // 1st retry.
+  FastForwardBy(GetFetchSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  // 2nd retry.
+  FastForwardBy(GetFetchSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  // 3rd retry.
+  FastForwardBy(GetFetchSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsFetchSettingsPendingAtBackend());
+
+  ReplyFetchSettingsAndAlbums(/*success=*/false);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+
+  // Will not retry.
+  FastForwardBy(GetFetchSettingsDelay() * 1.5);
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest, TestUpdateSettings) {
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/true);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest, TestUpdateSettingsTwice) {
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_TRUE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/true);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_TRUE(HasPendingUpdatesAtProvider());
+
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestUpdateSettingsFailedWillRetry) {
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestUpdateSettingsSecondRetryWillBackoff) {
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  base::TimeDelta delay1 = GetUpdateSettingsDelay();
+  FastForwardBy(delay1 * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  base::TimeDelta delay2 = GetUpdateSettingsDelay();
+  EXPECT_GT(delay2, delay1);
+
+  FastForwardBy(delay2 * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestUpdateSettingsWillNotRetryMoreThanThreeTimes) {
+  UpdateSettings();
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  // 1st retry.
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  // 2nd retry.
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  // 3rd retry.
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  ReplyUpdateSettings(/*success=*/false);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+
+  // Will not retry.
+  FastForwardBy(GetUpdateSettingsDelay() * 1.5);
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(HasPendingUpdatesAtProvider());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestNoFetchRequestWhenUpdatingSettings) {
+  EXPECT_FALSE(HasPendingFetchRequestAtProvider());
+  UpdateSettings();
+  EXPECT_FALSE(HasPendingFetchRequestAtProvider());
+
+  FetchSettings();
+  EXPECT_TRUE(HasPendingFetchRequestAtProvider());
+  EXPECT_FALSE(IsFetchSettingsPendingAtBackend());
+}
+
+TEST_F(PersonalizationAppAmbientProviderImplTest,
+       TestWeatherFalseTriggersUpdateSettings) {
+  ash::AmbientSettings weather_off_settings;
+  weather_off_settings.show_weather = false;
+
+  // Simulate initial page request with weather settings false. Because Ambient
+  // mode pref is enabled and |settings.show_weather| is false, this should
+  // trigger a call to |UpdateSettings| that sets |settings.show_weather| to
+  // true.
+  FetchSettings();
+  ReplyFetchSettingsAndAlbums(/*success=*/true, weather_off_settings);
+
+  // A call to |UpdateSettings| should have happened.
+  EXPECT_TRUE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_TRUE(IsUpdateSettingsPendingAtBackend());
+
+  ReplyUpdateSettings(/*success=*/true);
+
+  EXPECT_FALSE(IsUpdateSettingsPendingAtProvider());
+  EXPECT_FALSE(IsUpdateSettingsPendingAtBackend());
+
+  // |settings.show_weather| should now be true after the successful settings
+  // update.
+  EXPECT_TRUE(settings()->show_weather);
+}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 4c8cfee2..719faef 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -547,8 +547,10 @@
   ]
   if (is_chromeos_ash) {
     deps += [ "//remoting/host/chromeos:host_event_reporter_impl" ]
+    if (use_libfuzzer) {
+      deps += [ "//components/exo/wayland:weston_test_stub" ]
+    }
   }
-
   data_deps = [ ":dbus_service_files" ]
 
   allow_circular_includes_from = [
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index b40fc11..768154e 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -1172,8 +1172,7 @@
   }
 }
 
-// http://crbug.com/1294159
-IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, DISABLED_ClientHintsAlps) {
+IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, ClientHintsAlps) {
   base::HistogramTester histogram_tester;
   SetClientHintExpectationsOnMainFrame(true);
   ASSERT_TRUE(
@@ -1183,9 +1182,8 @@
       content::AcceptCHFrameRestart::kNavigationRestarted, 1);
 }
 
-// http://crbug.com/1294159
 IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest,
-                       DISABLED_ClientHintsAlpsNavigationPreload) {
+                       ClientHintsAlpsNavigationPreload) {
   SetClientHintExpectationsOnMainFrame(true);
   const GURL kCreateServiceWorker =
       GetHttp2Url("/service_worker/create_service_worker.html");
@@ -4060,8 +4058,16 @@
 constexpr char
     ThirdPartyAcceptChUaReducedOriginTrialBrowserTest::kThirdPartyOriginUrl[];
 
+// Flaky with ASAN. crbug.com/1293876
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ThirdPartyIframeUaReducedWithOriginTrialToken \
+  DISABLED_ThirdPartyIframeUaReducedWithOriginTrialToken
+#else
+#define MAYBE_ThirdPartyIframeUaReducedWithOriginTrialToken \
+  ThirdPartyIframeUaReducedWithOriginTrialToken
+#endif
 IN_PROC_BROWSER_TEST_F(ThirdPartyAcceptChUaReducedOriginTrialBrowserTest,
-                       ThirdPartyIframeUaReducedWithOriginTrialToken) {
+                       MAYBE_ThirdPartyIframeUaReducedWithOriginTrialToken) {
   const GURL top_level_frame_url =
       accept_ch_ua_reduced_cross_origin_iframe_request_url();
   // The first navigation is to opt-into the OT.
@@ -4072,8 +4078,16 @@
   EXPECT_EQ(GetLastRequestedURL()->path(), "/simple_3p_ot.html");
 }
 
+// Flaky with ASAN. crbug.com/1293876
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ThirdPartyIframeUaReducedWithAllCookiesBlocked \
+  DISABLED_ThirdPartyIframeUaReducedWithAllCookiesBlocked
+#else
+#define MAYBE_ThirdPartyIframeUaReducedWithAllCookiesBlocked \
+  ThirdPartyIframeUaReducedWithAllCookiesBlocked
+#endif
 IN_PROC_BROWSER_TEST_F(ThirdPartyAcceptChUaReducedOriginTrialBrowserTest,
-                       ThirdPartyIframeUaReducedWithAllCookiesBlocked) {
+                       MAYBE_ThirdPartyIframeUaReducedWithAllCookiesBlocked) {
   const GURL top_level_frame_url =
       accept_ch_ua_reduced_cross_origin_iframe_request_url();
   const GURL third_party_iframe_url = GURL(kThirdPartyOriginUrl);
@@ -4095,8 +4109,17 @@
   EXPECT_EQ(GetLastRequestedURL()->path(), "/simple_3p_ot.html");
 }
 
-IN_PROC_BROWSER_TEST_F(ThirdPartyAcceptChUaReducedOriginTrialBrowserTest,
-                       ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked) {
+// Flaky with ASAN. crbug.com/1293876
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked \
+  DISABLED_ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked
+#else
+#define MAYBE_ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked \
+  ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked
+#endif
+IN_PROC_BROWSER_TEST_F(
+    ThirdPartyAcceptChUaReducedOriginTrialBrowserTest,
+    MAYBE_ThirdPartyIframeUaReducedWithThirdPartyCookiesBlocked) {
   // Block third-party cookies.
   browser()->profile()->GetPrefs()->SetInteger(
       prefs::kCookieControlsMode,
@@ -4128,9 +4151,17 @@
       /*ch_ua_reduced_expected=*/true);
 }
 
+// Flaky with ASAN. crbug.com/1293876
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ThirdPartyIframeUaReducedWithSubresourceRedirectRequests \
+  DISABLED_ThirdPartyIframeUaReducedWithSubresourceRedirectRequests
+#else
+#define MAYBE_ThirdPartyIframeUaReducedWithSubresourceRedirectRequests \
+  ThirdPartyIframeUaReducedWithSubresourceRedirectRequests
+#endif
 IN_PROC_BROWSER_TEST_F(
     ThirdPartyAcceptChUaReducedOriginTrialBrowserTest,
-    ThirdPartyIframeUaReducedWithSubresourceRedirectRequests) {
+    MAYBE_ThirdPartyIframeUaReducedWithSubresourceRedirectRequests) {
   // The first navigation is to opt-into the OT.  Since there are subresource
   // requests, the last processed requests from the first navigation will have
   // the reduced UA string.
diff --git a/chrome/browser/component_updater/recovery_improved_component_installer.cc b/chrome/browser/component_updater/recovery_improved_component_installer.cc
index 47ffe35..454d3e1c 100644
--- a/chrome/browser/component_updater/recovery_improved_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_improved_component_installer.cc
@@ -183,8 +183,7 @@
 
 void RegisterRecoveryImprovedComponent(ComponentUpdateService* cus,
                                        PrefService* prefs) {
-// TODO(sorin): enable recovery component for macOS. crbug/687231.
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   DVLOG(1) << "Registering RecoveryImproved component.";
 
   // |cus| keeps a reference to the |installer| in the CrxComponent instance.
diff --git a/chrome/browser/component_updater/recovery_improved_component_installer_mac.cc b/chrome/browser/component_updater/recovery_improved_component_installer_mac.cc
new file mode 100644
index 0000000..5997ca8
--- /dev/null
+++ b/chrome/browser/component_updater/recovery_improved_component_installer_mac.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2022 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.
+
+#include "chrome/browser/component_updater/recovery_improved_component_installer.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/mac/foundation_util.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/update_client.h"
+#include "components/version_info/version_info.h"
+
+namespace component_updater {
+namespace {
+
+constexpr base::FilePath::CharType kRecoveryExecutableName[] =
+    FILE_PATH_LITERAL("ChromeRecovery");
+
+class RecoveryComponentActionHandlerMac
+    : public RecoveryComponentActionHandler {
+ public:
+  RecoveryComponentActionHandlerMac() = default;
+  RecoveryComponentActionHandlerMac(const RecoveryComponentActionHandlerMac&) =
+      delete;
+  RecoveryComponentActionHandlerMac& operator=(
+      const RecoveryComponentActionHandlerMac&) = delete;
+
+ private:
+  ~RecoveryComponentActionHandlerMac() override = default;
+
+  // Overrides for RecoveryComponentActionHandler.
+  base::CommandLine MakeCommandLine(
+      const base::FilePath& unpack_path) const override;
+  void Elevate(Callback callback) override;
+};
+
+base::CommandLine RecoveryComponentActionHandlerMac::MakeCommandLine(
+    const base::FilePath& unpack_path) const {
+  base::CommandLine command_line(unpack_path.Append(kRecoveryExecutableName));
+  command_line.AppendSwitchASCII("browser-version",
+                                 version_info::GetVersion().GetString());
+  command_line.AppendSwitchASCII("sessionid", session_id());
+  command_line.AppendSwitchASCII("appguid", base::mac::BaseBundleID());
+  return command_line;
+}
+
+void RecoveryComponentActionHandlerMac::Elevate(Callback callback) {
+  // Elevation is not yet supported on mac.
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), false, 0, 0));
+}
+
+}  // namespace
+
+scoped_refptr<update_client::ActionHandler>
+RecoveryComponentActionHandler::MakeActionHandler() {
+  return base::MakeRefCounted<RecoveryComponentActionHandlerMac>();
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/recovery_improved_component_unittest.cc b/chrome/browser/component_updater/recovery_improved_component_unittest.cc
index d9884547..4caca58 100644
--- a/chrome/browser/component_updater/recovery_improved_component_unittest.cc
+++ b/chrome/browser/component_updater/recovery_improved_component_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <cstdint>
 #include <iterator>
+#include <memory>
 #include <string>
 #include <utility>
 
@@ -83,64 +84,67 @@
 
 }  // namespace
 
-#if BUILDFLAG(IS_WIN)
-TEST_F(RecoveryImprovedActionHandlerTest, Handle) {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+TEST_F(RecoveryImprovedActionHandlerTest, HandleError) {
   unzip::SetUnzipperLaunchOverrideForTesting(
       base::BindRepeating(&unzip::LaunchInProcessUnzipper));
 
   // Tests the error is propagated through the callback in the error case.
-  {
-    base::RunLoop runloop;
-    base::MakeRefCounted<TestActionHandler>()->Handle(
-        base::FilePath{FILE_PATH_LITERAL("not-found")}, "some-session-id",
-        base::BindOnce(
-            [](base::OnceClosure quit_closure, bool succeeded, int error_code,
-               int extra_code1) {
-              EXPECT_FALSE(succeeded);
-              EXPECT_EQ(update_client::UnpackerError::kInvalidFile,
-                        static_cast<update_client::UnpackerError>(error_code));
-              EXPECT_EQ(2, extra_code1);
-              std::move(quit_closure).Run();
-            },
-            runloop.QuitClosure()));
-    runloop.Run();
-  }
+  base::RunLoop runloop;
+  base::MakeRefCounted<TestActionHandler>()->Handle(
+      base::FilePath{FILE_PATH_LITERAL("not-found")}, "some-session-id",
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, bool succeeded, int error_code,
+             int extra_code1) {
+            EXPECT_FALSE(succeeded);
+            EXPECT_EQ(update_client::UnpackerError::kInvalidFile,
+                      static_cast<update_client::UnpackerError>(error_code));
+            EXPECT_EQ(2, extra_code1);
+            std::move(quit_closure).Run();
+          },
+          runloop.QuitClosure()));
+  runloop.Run();
+}
+#endif  //  BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+
+#if BUILDFLAG(IS_WIN)
+TEST_F(RecoveryImprovedActionHandlerTest, HandleSuccess) {
+  unzip::SetUnzipperLaunchOverrideForTesting(
+      base::BindRepeating(&unzip::LaunchInProcessUnzipper));
 
   // Tests that the recovery program runs and it returns an expected value.
-  {
-    constexpr char kActionRunFileName[] = "ChromeRecovery.crx3";
-    base::FilePath from_path;
-    base::PathService::Get(base::DIR_SOURCE_ROOT, &from_path);
-    from_path = from_path.AppendASCII("components")
-                    .AppendASCII("test")
-                    .AppendASCII("data")
-                    .AppendASCII("update_client")
-                    .AppendASCII(kActionRunFileName);
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    const base::FilePath to_path =
-        temp_dir_.GetPath().AppendASCII(kActionRunFileName);
-    ASSERT_TRUE(base::CopyFile(from_path, to_path));
+  constexpr char kActionRunFileName[] = "ChromeRecovery.crx3";
+  base::FilePath from_path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &from_path);
+  from_path = from_path.AppendASCII("components")
+                  .AppendASCII("test")
+                  .AppendASCII("data")
+                  .AppendASCII("update_client")
+                  .AppendASCII(kActionRunFileName);
+  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  const base::FilePath to_path =
+      temp_dir_.GetPath().AppendASCII(kActionRunFileName);
+  ASSERT_TRUE(base::CopyFile(from_path, to_path));
 
-    base::RunLoop runloop;
-    base::MakeRefCounted<TestActionHandler>()->Handle(
-        to_path, "some-session-id",
-        base::BindOnce(
-            [](base::OnceClosure quit_closure, bool succeeded, int error_code,
-               int extra_code1) {
-              EXPECT_TRUE(succeeded);
-              EXPECT_EQ(1877345072, error_code);
-              EXPECT_EQ(0, extra_code1);
-              std::move(quit_closure).Run();
-            },
-            runloop.QuitClosure()));
-    {
-      // For some reason, the task which runs the wait for the recovery EXE
-      // execution is handled with some delay. This causes the run loop to
-      // fail with a timeout.
-      const base::test::ScopedRunLoopTimeout specific_timeout(
-          FROM_HERE, base::Seconds(60));
-      runloop.Run();
-    }
+  base::RunLoop runloop;
+  base::MakeRefCounted<TestActionHandler>()->Handle(
+      to_path, "some-session-id",
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, bool succeeded, int error_code,
+             int extra_code1) {
+            EXPECT_TRUE(succeeded);
+            EXPECT_EQ(1877345072, error_code);
+            EXPECT_EQ(0, extra_code1);
+            std::move(quit_closure).Run();
+          },
+          runloop.QuitClosure()));
+  {
+    // For some reason, the task which runs the wait for the recovery EXE
+    // execution is handled with some delay. This causes the run loop to
+    // fail with a timeout.
+    const base::test::ScopedRunLoopTimeout specific_timeout(FROM_HERE,
+                                                            base::Seconds(60));
+    runloop.Run();
   }
 }
 #endif  //  OS_WIN
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index de59885..e03189a 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -47,11 +47,13 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #endif  // BUILDFLAG(IS_WIN)
 
-#if BUILDFLAG(IS_WIN)
-#include "chrome/browser/component_updater/recovery_improved_component_installer.h"
-#else
+#if BUILDFLAG(IS_MAC)
 #include "chrome/browser/component_updater/recovery_component_installer.h"
-#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_MAC)
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#include "chrome/browser/component_updater/recovery_improved_component_installer.h"
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/component_updater/desktop_sharing_hub_component_remover.h"
@@ -96,11 +98,13 @@
 
 #if BUILDFLAG(IS_WIN)
   RegisterRecoveryImprovedComponent(cus, g_browser_process->local_state());
-#else
-  // TODO(crbug.com/687231): Implement the Improved component on Mac, etc.
-  RegisterRecoveryComponent(cus, g_browser_process->local_state());
 #endif  // BUILDFLAG(IS_WIN)
 
+#if BUILDFLAG(IS_MAC)
+  RegisterRecoveryImprovedComponent(cus, g_browser_process->local_state());
+  RegisterRecoveryComponent(cus, g_browser_process->local_state());
+#endif  // BUILDFLAG(IS_MAC)
+
 #if BUILDFLAG(ENABLE_MEDIA_FOUNDATION_WIDEVINE_CDM)
   RegisterMediaFoundationWidevineCdmComponent(cus);
 #endif
diff --git a/chrome/browser/extensions/api/tabs/execute_script_apitest.cc b/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
index cba65434..32be036 100644
--- a/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
+++ b/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
@@ -181,13 +181,25 @@
   }
 };
 
+// Flaky on ASAN. crbug.com/1293865
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_SynchronousRemoval DISABLED_SynchronousRemoval
+#else
+#define MAYBE_SynchronousRemoval SynchronousRemoval
+#endif
 // Removes the frame as soon as the content script is executed.
-IN_PROC_BROWSER_TEST_P(DestructiveScriptTest, SynchronousRemoval) {
+IN_PROC_BROWSER_TEST_P(DestructiveScriptTest, MAYBE_SynchronousRemoval) {
   ASSERT_TRUE(RunSubtest("synchronous")) << message_;
 }
 
+// Flaky on ASAN. crbug.com/1293865
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_MicrotaskRemoval DISABLED_MicrotaskRemoval
+#else
+#define MAYBE_MicrotaskRemoval MicrotaskRemoval
+#endif
 // Removes the frame at the frame's first scheduled microtask.
-IN_PROC_BROWSER_TEST_P(DestructiveScriptTest, MicrotaskRemoval) {
+IN_PROC_BROWSER_TEST_P(DestructiveScriptTest, MAYBE_MicrotaskRemoval) {
   ASSERT_TRUE(RunSubtest("microtask")) << message_;
 }
 
diff --git a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
index 22b875b..dff0963d 100644
--- a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
+++ b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
@@ -60,10 +60,8 @@
                                 shill::kBroadcastAddressParameterThirdPartyVpn,
                                 shill::kDomainSearchParameterThirdPartyVpn};
 
-void DoNothingFailureCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  EXPECT_EQ(true, false);
+void DoNothingFailureCallback(const std::string& error_name) {
+  FAIL();
 }
 
 void DoNothingSuccessCallback(const std::string& service_path,
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index a5fd993b..eea280e 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -489,6 +489,19 @@
       frame_navigation_state->GetErrorOccurredInFrame();
   frame_details.parent_frame_id =
       ExtensionApiFrameIdMap::GetParentFrameId(render_frame_host);
+  frame_details.document_id =
+      ExtensionApiFrameIdMap::GetDocumentId(render_frame_host).ToString();
+  // Only set the parentDocumentId value if we have a parent.
+  if (content::RenderFrameHost* parent_frame_host =
+          render_frame_host->GetParentOrOuterDocument()) {
+    frame_details.parent_document_id = std::make_unique<std::string>(
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+  }
+  frame_details.frame_type =
+      ToString(ExtensionApiFrameIdMap::GetFrameType(render_frame_host));
+  frame_details.document_lifecycle =
+      ToString(ExtensionApiFrameIdMap::GetDocumentLifecycle(render_frame_host));
+
   return RespondNow(ArgumentList(GetFrame::Results::Create(frame_details)));
 }
 
@@ -531,6 +544,19 @@
         frame.frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host);
         frame.parent_frame_id =
             ExtensionApiFrameIdMap::GetParentFrameId(render_frame_host);
+        frame.document_id =
+            ExtensionApiFrameIdMap::GetDocumentId(render_frame_host).ToString();
+        // Only set the parentDocumentId value if we have a parent.
+        if (content::RenderFrameHost* parent_frame_host =
+                render_frame_host->GetParentOrOuterDocument()) {
+          frame.parent_document_id = std::make_unique<std::string>(
+              ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host)
+                  .ToString());
+        }
+        frame.frame_type =
+            ToString(ExtensionApiFrameIdMap::GetFrameType(render_frame_host));
+        frame.document_lifecycle = ToString(
+            ExtensionApiFrameIdMap::GetDocumentLifecycle(render_frame_host));
         frame.process_id = render_frame_host->GetProcess()->GetID();
         frame.error_occurred = navigation_state->GetErrorOccurredInFrame();
         result_list.push_back(std::move(frame));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f66bc9a2..0b5659d8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3444,8 +3444,8 @@
   },
   {
     "name": "global-media-controls-cast-start-stop",
-    "owners": [ "jrw", "takumif", "muyaoxu@google.com", "openscreen-eng@google.com" ],
-    "expiry_milestone": 99
+    "owners": [  "takumif", "muyaoxu@google.com", "openscreen-eng@google.com" ],
+    "expiry_milestone": 105
   },
   {
     "name": "global-media-controls-modern-ui",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index eb153d4..1acc5453 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5344,7 +5344,7 @@
 
 const char kWindowControlMenu[] = "Float current active window";
 const char kWindowControlMenuDescription[] =
-    "Enables the accelerator (Control + Alt + F) to float current active "
+    "Enables the accelerator (Command + Alt + F) to float current active "
     "window.";
 
 const char kLauncherNudgeName[] = "Enable launcher nudge";
diff --git a/chrome/browser/headless/headless_mode_browsertest.cc b/chrome/browser/headless/headless_mode_browsertest.cc
index 470f3a9..1654bb1 100644
--- a/chrome/browser/headless/headless_mode_browsertest.cc
+++ b/chrome/browser/headless/headless_mode_browsertest.cc
@@ -6,10 +6,10 @@
 
 #include "build/build_config.h"
 
-// Native headless is currently available only on Linux and Windows platforms.
+// Native headless is currently available on Linux, Windows and Mac platforms.
 // More platforms will be added later, so avoid function level clutter by
 // providing a compile time condition over the entire file.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
 #include <memory>
 #include <string>
@@ -78,12 +78,13 @@
 }
 #endif  // BUILDFLAG(IS_LINUX)
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTest, BrowserDesktopWindowHidden) {
-  // On Windows, the Native Headless Chrome browser window exists but is hidden.
+  // On Windows and Mac, the Native Headless Chrome browser window exists but is
+  // hidden.
   EXPECT_FALSE(browser()->window()->IsVisible());
 }
-#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
 class HeadlessModeBrowserTestWithUserDataDir : public HeadlessModeBrowserTest {
  public:
@@ -167,4 +168,4 @@
   return static_cast<int>(notify_result);
 }
 
-#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index a8d544f..8400367 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -32,14 +32,21 @@
                                  base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kCastAllowAllIPsFeature{"CastAllowAllIPs",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kGlobalMediaControlsCastStartStop{
-    "GlobalMediaControlsCastStartStop", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kAllowAllSitesToInitiateMirroring{
     "AllowAllSitesToInitiateMirroring", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kDialMediaRouteProvider{"DialMediaRouteProvider",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kDialEnforceUrlIPAddress{"DialEnforceUrlIPAddress",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
+
+#if BUILDFLAG(IS_CHROMEOS)
+const base::Feature kGlobalMediaControlsCastStartStop{
+    "GlobalMediaControlsCastStartStop", base::FEATURE_DISABLED_BY_DEFAULT};
+#else
+const base::Feature kGlobalMediaControlsCastStartStop{
+    "GlobalMediaControlsCastStartStop", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace {
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
index e36f7b4..1b3a099 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -142,6 +142,25 @@
                          browser()->profile()));
 }
 
+class PageContentAnnotationsServiceValidationBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  PageContentAnnotationsServiceValidationBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {features::kOptimizationHints, features::kBatchAnnotationsValidation},
+        {features::kPageContentAnnotations});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceValidationBrowserTest,
+                       ValidationEnablesService) {
+  EXPECT_NE(nullptr, PageContentAnnotationsServiceFactory::GetForProfile(
+                         browser()->profile()));
+}
+
 class PageContentAnnotationsServiceBrowserTest : public InProcessBrowserTest {
  public:
   PageContentAnnotationsServiceBrowserTest() {
@@ -609,7 +628,7 @@
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
-  
+
 // TODO(crbug/1291486): Disabled due to flakiness on Mac and Windows.
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 #define MAYBE_QueueFullAndVisitBatchActive DISABLED_QueueFullAndVisitBatchActive
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
index 2d0ba01..c13e631 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
@@ -48,8 +48,12 @@
 
 KeyedService* PageContentAnnotationsServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  if (!optimization_guide::features::IsPageContentAnnotationEnabled())
+  // Allow for the validation experiment to enable the PCAService without need
+  // to enable both features.
+  if (!optimization_guide::features::IsPageContentAnnotationEnabled() &&
+      !optimization_guide::features::BatchAnnotationsValidationEnabled()) {
     return nullptr;
+  }
 
   Profile* profile = Profile::FromBrowserContext(context);
 
diff --git a/chrome/browser/policy/local_sync_policy_handler.cc b/chrome/browser/policy/local_sync_policy_handler.cc
index 294fd109..342a1e9 100644
--- a/chrome/browser/policy/local_sync_policy_handler.cc
+++ b/chrome/browser/policy/local_sync_policy_handler.cc
@@ -28,7 +28,8 @@
                                                  PrefValueMap* prefs) {
   const base::Value* value = policies.GetValue(policy_name());
   std::string string_value;
-  if (value && value->GetAsString(&string_value)) {
+  if (value && value->is_string()) {
+    string_value = value->GetString();
     base::FilePath::StringType expanded_value =
 #if BUILDFLAG(IS_WIN)
         policy::path_parser::ExpandPathVariables(
diff --git a/chrome/browser/policy/local_sync_policy_handler_unittest.cc b/chrome/browser/policy/local_sync_policy_handler_unittest.cc
index 43a9055..e5a60bf 100644
--- a/chrome/browser/policy/local_sync_policy_handler_unittest.cc
+++ b/chrome/browser/policy/local_sync_policy_handler_unittest.cc
@@ -46,8 +46,8 @@
 
   const base::Value* value;
   ASSERT_TRUE(prefs_.GetValue(syncer::prefs::kLocalSyncBackendDir, &value));
-  std::string out;
-  ASSERT_TRUE(value->GetAsString(&out));
+  ASSERT_TRUE(value->is_string());
+  const std::string& out = value->GetString();
   EXPECT_NE(std::string::npos, out.find("foo"));
   EXPECT_EQ(std::string::npos, out.find("${user_name}"));
 }
diff --git a/chrome/browser/policy/test/web_rtc_udp_port_range_policy_browsertest.cc b/chrome/browser/policy/test/web_rtc_udp_port_range_policy_browsertest.cc
index 1ed8131..ceb56bf 100644
--- a/chrome/browser/policy/test/web_rtc_udp_port_range_policy_browsertest.cc
+++ b/chrome/browser/policy/test/web_rtc_udp_port_range_policy_browsertest.cc
@@ -54,7 +54,8 @@
   const PrefService::Preference* pref =
       user_prefs::UserPrefs::Get(browser()->profile())
           ->FindPreference(prefs::kWebRTCUDPPortRange);
-  pref->GetValue()->GetAsString(&port_range);
+  if (pref->GetValue()->is_string())
+    port_range = pref->GetValue()->GetString();
   EXPECT_EQ(kTestWebRtcUdpPortRange, port_range);
 }
 
@@ -64,7 +65,8 @@
   const PrefService::Preference* pref =
       user_prefs::UserPrefs::Get(browser()->profile())
           ->FindPreference(prefs::kWebRTCUDPPortRange);
-  pref->GetValue()->GetAsString(&port_range);
+  if (pref->GetValue()->is_string())
+    port_range = pref->GetValue()->GetString();
   EXPECT_TRUE(port_range.empty());
 }
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index ff400ec1..49ce6e9 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -724,6 +724,10 @@
 const char kDataReductionProxyLastEnabledTime[] =
     "data_reduction.last_enabled_time";
 
+// Deprecated 02/2022.
+const char kStabilityChildProcessCrashCount[] =
+    "user_experience_metrics.stability.child_process_crash_count";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -761,6 +765,7 @@
   registry->RegisterIntegerPref(kStabilityRendererHangCount, 0);
   registry->RegisterIntegerPref(kStabilityIncompleteSessionEndCount, 0);
   registry->RegisterBooleanPref(kStabilitySessionEndCompleted, true);
+  registry->RegisterIntegerPref(kStabilityChildProcessCrashCount, 0);
 }
 
 // Register prefs used only for migration (clearing or moving to a new key).
diff --git a/chrome/browser/resources/accessibility/accessibility.css b/chrome/browser/resources/accessibility/accessibility.css
index ee0221a9..d112f46 100644
--- a/chrome/browser/resources/accessibility/accessibility.css
+++ b/chrome/browser/resources/accessibility/accessibility.css
@@ -19,13 +19,12 @@
 }
 
 .row {
-  border-bottom: 1px solid #A0A0A0;
+  border-bottom: 1px solid rgb(128, 134, 139);
   padding: 5px;
 }
 
 .url {
-  color: #000;
-  opacity: 0.60;
+  color: rgb(128, 134, 139);
 }
 
 p {
@@ -54,9 +53,8 @@
 }
 
 .secondary {
-  color: #000;
+  color: rgb(60, 64, 67);
   margin: 0 20px 12px 32px;
-  opacity: 0.60;
 }
 
 .hidden {
@@ -76,8 +74,7 @@
 }
 
 label.disabled {
-  color: #000;
-  opacity: 0.38;
+  color: rgb(128, 134, 139);
 }
 
 input[type='checkbox'] {
diff --git a/chrome/browser/resources/app_settings/app.html b/chrome/browser/resources/app_settings/app.html
index df20ba0..df85b62c 100644
--- a/chrome/browser/resources/app_settings/app.html
+++ b/chrome/browser/resources/app_settings/app.html
@@ -21,6 +21,11 @@
     margin-inline-start: 2px;
     width: 36px;
   }
+
+  #uninstall-button {
+    /* Push the button to the right in flex layout. */
+    margin-inline-start: auto;
+  }
 </style>
 <cr-toolbar page-name="$i18n{title}" show-search="[[showSearch_]]">
 </cr-toolbar>
@@ -28,6 +33,11 @@
   <div class="cr-row first" id="headerLine">
     <img id="title-icon" src="[[iconUrl_]]" aria-hidden="true">
     <h1 class="cr-title-text">[[app_.title]]</h1>
+    <app-management-uninstall-button id="uninstall-button"
+        app="[[app_]]"
+        uninstall-label="$i18n{appManagementUninstallLabel}"
+        policy-label="$i18n{appManagementAppInstalledByPolicyLabel}">
+    </app-management-uninstall-button>
   </div>
   <div class="permission-list">
     <app-management-run-on-os-login-item
@@ -41,7 +51,7 @@
       app="[[app_]]">
     </app-management-window-mode>
     <app-management-permission-item
-        class="permission-card-row separated-row header-text"
+        class="permission-card-row separated-row"
         app="[[app_]]"
         permission-label="$i18n{appManagementNotificationsLabel}"
         permission-type="kNotifications">
diff --git a/chrome/browser/resources/app_settings/app.ts b/chrome/browser/resources/app_settings/app.ts
index 2404894..0415de8 100644
--- a/chrome/browser/resources/app_settings/app.ts
+++ b/chrome/browser/resources/app_settings/app.ts
@@ -9,6 +9,7 @@
 import 'chrome://resources/cr_components/app_management/permission_item.js';
 import 'chrome://resources/cr_components/app_management/window_mode_item.js';
 import 'chrome://resources/cr_components/app_management/icons.js';
+import 'chrome://resources/cr_components/app_management/uninstall_button.js';
 
 import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
index 3c6cccb..6fc9b45df 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -10,20 +10,16 @@
 import {SpeechParser} from './parse/speech_parser.js';
 
 const ErrorEvent = chrome.speechRecognitionPrivate.SpeechRecognitionErrorEvent;
+const HintType = chrome.accessibilityPrivate.DictationBubbleHintType;
+const IconType = chrome.accessibilityPrivate.DictationBubbleIconType;
 const ResultEvent =
     chrome.speechRecognitionPrivate.SpeechRecognitionResultEvent;
 const StartOptions = chrome.speechRecognitionPrivate.StartOptions;
 const StopEvent = chrome.speechRecognitionPrivate.SpeechRecognitionStopEvent;
 const SpeechRecognitionType =
     chrome.speechRecognitionPrivate.SpeechRecognitionType;
-const IconType = chrome.accessibilityPrivate.DictationBubbleIconType;
-const HintType = chrome.accessibilityPrivate.DictationBubbleHintType;
 
-/**
- * Main class for the Chrome OS dictation feature.
- * Please note: this is being developed behind the flag
- * --enable-experimental-accessibility-dictation-extension
- */
+/** Main class for the Chrome OS dictation feature. */
 export class Dictation {
   constructor() {
     /** @private {InputController} */
@@ -403,8 +399,7 @@
    * @private
    */
   setInterimText_(text) {
-    if (this.chromeVoxEnabled_ || !this.commandsFeatureEnabled_) {
-      // Using chrome.input.ime for UI causes too much verbosity with ChromeVox.
+    if (!this.commandsFeatureEnabled_) {
       return;
     }
 
@@ -428,8 +423,7 @@
    * @private
    */
   clearInterimText_() {
-    if (this.chromeVoxEnabled_ || !this.commandsFeatureEnabled_) {
-      // Using chrome.input.ime for UI causes too much verbosity with ChromeVox.
+    if (!this.commandsFeatureEnabled_) {
       return;
     }
 
@@ -449,8 +443,7 @@
    * @private
    */
   showMacroExecuted_(macro, transcript) {
-    if (this.chromeVoxEnabled_ || !this.commandsFeatureEnabled_) {
-      // Using chrome.input.ime for UI causes too much verbosity with ChromeVox.
+    if (!this.commandsFeatureEnabled_) {
       return;
     }
 
@@ -480,8 +473,7 @@
    * @private
    */
   showMacroExecutionFailed_(macro, transcript) {
-    if (this.chromeVoxEnabled_ || !this.commandsFeatureEnabled_) {
-      // Using chrome.input.ime for UI causes too much verbosity with ChromeVox.
+    if (!this.commandsFeatureEnabled_) {
       return;
     }
 
@@ -516,7 +508,7 @@
    * @private
    */
   hideCommandsUI_() {
-    if (this.chromeVoxEnabled_ || !this.commandsFeatureEnabled_) {
+    if (!this.commandsFeatureEnabled_) {
       return;
     }
 
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
index b785970e..4602bf46 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
@@ -135,6 +135,26 @@
         padding-top: 0;
       }
 
+      [read-more-content=true] {
+        -webkit-mask-image: linear-gradient(180deg, #FFF 95%, transparent);
+        overflow-y: hidden;
+      }
+
+      #readMoreButtonContainer {
+        display: flex;
+        justify-content: center;
+        position: relative;
+        top: -16px;
+      }
+
+      #readMoreButton {
+        border-radius: 50%;
+        min-height: 32px;
+        min-width: 32px;
+        padding: 0;
+        transform: rotate(90deg);
+      }
+
       #oobe-title {
         padding-top: var(--oobe-adaptive-dialog-title-top-padding);
       }
@@ -221,10 +241,19 @@
             <div id="contentContainer" class="layout vertical flex">
               <slot name="content"></slot>
             </div>
+            <div id="readMoreButtonContainer">
+              <template is="dom-if" if="[[showReadMoreButton_]]" restamp>
+                <cr-button id="readMoreButton" on-click="onReadMoreClick_"
+                    class="action-button">
+                  <iron-icon icon="oobe-20:button-arrow-forward"></iron-icon>
+                </cr-button>
+              </template>
+            </div>
           </div>
         </div>
         <div class="bottom-buttons-container vertical-mode-centering">
-          <slot class="layout horizontal end-justified" name="bottom-buttons">
+          <slot class="layout horizontal end-justified" name="bottom-buttons"
+              hidden="[[showReadMoreButton_]]">
           </slot>
         </div>
       </template>
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
index 7d97cc3..2330c30 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
@@ -2,6 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * Indicates `Read more` button state (listed in upgrade order).
+ * @enum {string}
+ */
+const ReadMoreState = {
+  UNKNOWN: 'unknown',
+  SHOWN: 'shown',
+  HIDDEN: 'hidden',
+};
+
 Polymer({
   is: 'oobe-adaptive-dialog',
 
@@ -14,31 +24,80 @@
       value: false,
       observer: 'onNoLazyChanged_',
     },
+
+    /**
+     * If set, when content overflows, there will be no scrollbar initially.
+     * A `Read more` button will be shown and the bottom buttons will be hidden
+     * until the `Read More` button is clicked to ensure that the user sees all
+     * the content before proceeding.
+     * When readMore is set to true, it does not necessarily mean that the
+     * `Read more` button will be shown, It will only be shown if the content
+     * overflows.
+     */
+    readMore: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
+     * if readMore is set to true and the content overflows contentContainer,
+     * showReadMoreButton_ will be set to true to show the `Read more` button
+     * and hide the bottom buttons.
+     * Once overflown content is shown, either by zooming out, tabbing to hidden
+     * content or by clicking the `Read more` button, this property should be
+     * set back to false.
+     * Don't change it directly, call addReadMoreButton_ and
+     * removeReadMoreButton_.
+     * @private
+     */
+    showReadMoreButton_: {
+      type: Boolean,
+      value: false,
+    }
   },
 
   /**
    * Creates a ResizeObserver and attaches it to the relevant containers
    * to be observed on size changes and scroll position.
+   * @private
    */
-  observeScrolling_() {
+  addResizeObserver_() {
     if (this.resizeObserver_) {  // Already observing
       return;
     }
 
+    // If `Read more` button is not set, upgrade the state directly to hidden,
+    // otherwise, the state will stay unknown until the content is redndered.
+    if (this.readMore)
+      this.readMoreState = ReadMoreState.UNKNOWN;
+    else
+      this.readMoreState = ReadMoreState.HIDDEN;
+
     var scrollContainer = this.shadowRoot.querySelector('#scrollContainer');
     var contentContainer = this.shadowRoot.querySelector('#contentContainer');
     if (!scrollContainer || !contentContainer) {
       return;
     }
 
-    this.resizeObserver_ =
-        new ResizeObserver(() => void this.applyScrollClassTags_());
+    this.resizeObserver_ = new ResizeObserver(() => void this.onResize_());
     this.resizeObserver_.observe(scrollContainer);
     this.resizeObserver_.observe(contentContainer);
   },
 
+  /** @private */
+  onResize_() {
+    this.maybeUpgradeReadMoreState_(false /* read_more_clicked */);
+
+    // Apply scroll tags when `Read more` button is hidden.
+    if (this.readMoreState == ReadMoreState.HIDDEN) {
+      this.applyScrollClassTags_();
+    }
+  },
+
   /**
-   * Applies the class tags to scrollContainer that control the shadows.
+   * Applies the class tags to scrollContainer that control the shadows, and
+   * updates the `Read more` button state if needed.
+   * @private
    */
   applyScrollClassTags_() {
     var el = this.shadowRoot.querySelector('#scrollContainer');
@@ -49,6 +108,45 @@
         el.scrollTop + el.clientHeight >= el.scrollHeight);
   },
 
+  /**
+   * Upgrades the `Read More` button State if needed.
+   * UNKNOWN -> SHOWN:  If the content overflows the content container.
+   * UNKNOWN -> HIDDEN: If the content does not overflow the content container.
+   * SHOWN   -> HIDDEN: If `Read more` is clicked, the content stopped
+   * overflowing the content container or the container is scrolled.
+   *
+   * @param {boolean} read_more_clicked Whether the `Read more` button clicked
+   *     or not.
+   * @private
+   */
+  maybeUpgradeReadMoreState_(read_more_clicked) {
+    // HIDDEN is the final state. We cannot move from HIDDEN state to SHOWN or
+    // UNKNOWN state.
+    if (this.readMoreState == ReadMoreState.HIDDEN)
+      return;
+
+    if (read_more_clicked) {
+      this.readMoreState = ReadMoreState.HIDDEN;
+      this.removeReadMoreButton_();
+      return;
+    }
+    var content = this.shadowRoot.querySelector('#contentContainer');
+    if (this.readMoreState == ReadMoreState.UNKNOWN) {
+      if (content.clientHeight < content.scrollHeight) {
+        this.readMoreState = ReadMoreState.SHOWN;
+        this.addReadMoreButton_();
+      } else {
+        this.readMoreState = ReadMoreState.HIDDEN;
+      }
+    } else if (this.readMoreState == ReadMoreState.SHOWN) {
+      if (content.clientHeight >= content.scrollHeight ||
+          content.scrollTop > 0) {
+        this.readMoreState = ReadMoreState.HIDDEN;
+        this.removeReadMoreButton_();
+      }
+    }
+  },
+
   focus() {
     /* When Network Selection Dialog is shown because user pressed "Back"
        button on EULA screen, display_manager does not inform this dialog that
@@ -61,7 +159,7 @@
 
   onBeforeShow() {
     this.shadowRoot.querySelector('#lazy').get();
-    this.observeScrolling_();
+    this.addResizeObserver_();
   },
 
   /**
@@ -86,10 +184,8 @@
     element.focus();
   },
 
-  /**
-   * This is called when this dialog is shown.
-   */
-  show() {
+  /** @private */
+  focusOnShow_() {
     var focusedElements = this.getElementsByClassName('focus-on-show');
     var focused = false;
     for (var i = 0; i < focusedElements.length; ++i) {
@@ -105,7 +201,13 @@
       Polymer.RenderStatus.afterNextRender(
           this, () => this.focusElement_(focusedElements[0]));
     }
+  },
 
+  /**
+   * This is called when this dialog is shown.
+   */
+  show() {
+    this.focusOnShow_();
     this.fire('show-dialog');
   },
 
@@ -113,5 +215,49 @@
   onNoLazyChanged_() {
     if (this.noLazy)
       this.shadowRoot.querySelector('#lazy').get();
+  },
+
+  /** @private */
+  addReadMoreButton_() {
+    var contentContainer = this.shadowRoot.querySelector('#contentContainer');
+    contentContainer.setAttribute('read-more-content', true);
+    this.showReadMoreButton_ = true;
+
+    Polymer.RenderStatus.afterNextRender(this, () => {
+      var readMoreButton = this.shadowRoot.querySelector('#readMoreButton');
+      this.focusElement_(readMoreButton);
+    });
+
+    // Once a tab reaches an element outside of the visible area, call
+    // maybeUpgradeReadMoreState_ to apply changes.
+    contentContainer.addEventListener('keyup', (event) => {
+      if (!this.showReadMoreButton_)
+        return;
+      if (event.which === 9) {
+        if (contentContainer.scrollTop > 0) {
+          this.maybeUpgradeReadMoreState_(true /* read_more_clicked */);
+        }
+      }
+    });
+  },
+
+  /** @private */
+  removeReadMoreButton_() {
+    var contentContainer = this.shadowRoot.querySelector('#contentContainer');
+    contentContainer.removeAttribute('read-more-content');
+    this.showReadMoreButton_ = false;
+
+    // If `read more` button is focused after it was removed, move focus to the
+    // 'focus-on-show' element.
+    var readMoreButton = this.shadowRoot.querySelector('#readMoreButton');
+    if (this.shadowRoot.activeElement == readMoreButton)
+      this.focusOnShow_();
+
+    this.scrollToBottom();
+  },
+
+  /** @private */
+  onReadMoreClick_() {
+    this.maybeUpgradeReadMoreState_(true /* read_more_clicked */);
   }
 });
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
index 9965e66..87f310b 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
@@ -180,7 +180,8 @@
 
     <!-- LOADED DIALOG -->
     <oobe-adaptive-dialog id="loadedDialog" for-step="loaded" role="dialog"
-        aria-label$="[[i18nDynamic(locale, 'consolidatedConsentHeader')]]">
+        aria-label$="[[i18nDynamic(locale, 'consolidatedConsentHeader')]]"
+        read-more>
       <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
       <h1 slot="title">
         [[getTitle_(locale, isEnterpriseManagedAccount_, isChildAccount_)]]
diff --git a/chrome/browser/resources/history/app.ts b/chrome/browser/resources/history/app.ts
index 4c11ea15..1a26df3 100644
--- a/chrome/browser/resources/history/app.ts
+++ b/chrome/browser/resources/history/app.ts
@@ -35,7 +35,7 @@
 import {HistoryListElement} from './history_list.js';
 import {HistoryToolbarElement} from './history_toolbar.js';
 import {Page, TABBED_PAGES} from './router.js';
-import {FooterInfo} from './side_bar.js';
+import {FooterInfo, HistorySideBarElement} from './side_bar.js';
 
 let lazyLoadPromise: Promise<void>|null = null;
 export function ensureLazyLoaded(): Promise<void> {
@@ -119,6 +119,7 @@
 export interface HistoryAppElement {
   $: {
     'content': IronPagesElement,
+    'content-side-bar': HistorySideBarElement,
     'drawer': CrLazyRenderElement<CrDrawerElement>,
     'history': HistoryListElement,
     'tabs-container': Element,
@@ -568,4 +569,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-app': HistoryAppElement;
+  }
+}
+
 customElements.define(HistoryAppElement.is, HistoryAppElement);
diff --git a/chrome/browser/resources/history/history.ts b/chrome/browser/resources/history/history.ts
index 9a5d8fa..832a79c 100644
--- a/chrome/browser/resources/history/history.ts
+++ b/chrome/browser/resources/history/history.ts
@@ -4,10 +4,13 @@
 
 import './app.js';
 
-export {ensureLazyLoaded, listenForPrivilegedLinkClicks} from './app.js';
+export {ensureLazyLoaded, HistoryAppElement, listenForPrivilegedLinkClicks} from './app.js';
 export {BrowserService, BrowserServiceImpl, QueryResult, RemoveVisitsRequest} from './browser_service.js';
 export {HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from './constants.js';
 export {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryEntry, HistoryQuery} from './externs.js';
 export {BrowserProxyImpl} from './history_clusters/browser_proxy.js';
 export {PageCallbackRouter, PageHandlerRemote} from './history_clusters/history_clusters.mojom-webui.js';
-export {MetricsProxyImpl} from './history_clusters/metrics_proxy.js';
+export {ClusterAction, RelatedSearchAction, VisitAction, VisitType} from './history_clusters/metrics_proxy.js';
+export {MetricsProxy, MetricsProxyImpl} from './history_clusters/metrics_proxy.js';
+export {HistorySearchedLabelElement} from './searched_label.js';
+export {HistorySideBarElement} from './side_bar.js';
diff --git a/chrome/browser/resources/history/history_item.ts b/chrome/browser/resources/history/history_item.ts
index ec5a991d..38ea150d 100644
--- a/chrome/browser/resources/history/history_item.ts
+++ b/chrome/browser/resources/history/history_item.ts
@@ -363,6 +363,12 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-item': HistoryItemElement;
+  }
+}
+
 customElements.define(HistoryItemElement.is, HistoryItemElement);
 
 /**
diff --git a/chrome/browser/resources/history/history_list.ts b/chrome/browser/resources/history/history_list.ts
index c146060..e38ef02c 100644
--- a/chrome/browser/resources/history/history_list.ts
+++ b/chrome/browser/resources/history/history_list.ts
@@ -7,6 +7,7 @@
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
 import './shared_style.js';
+import './history_item.js';
 
 import {CrA11yAnnouncerElement} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
diff --git a/chrome/browser/resources/history/history_toolbar.html b/chrome/browser/resources/history/history_toolbar.html
index f6bfc11..dea5738 100644
--- a/chrome/browser/resources/history/history_toolbar.html
+++ b/chrome/browser/resources/history/history_toolbar.html
@@ -26,7 +26,7 @@
         opacity: 1;
       }
     </style>
-    <cr-toolbar id="main-toolbar"
+    <cr-toolbar id="mainToolbar"
         has-overlay$="[[itemsSelected_]]"
         page-name="$i18n{title}"
         clear-label="$i18n{clearSearch}"
diff --git a/chrome/browser/resources/history/history_toolbar.ts b/chrome/browser/resources/history/history_toolbar.ts
index 3fe0d63..e7e8322 100644
--- a/chrome/browser/resources/history/history_toolbar.ts
+++ b/chrome/browser/resources/history/history_toolbar.ts
@@ -17,7 +17,7 @@
 
 export interface HistoryToolbarElement {
   $: {
-    'main-toolbar': CrToolbarElement,
+    mainToolbar: CrToolbarElement,
   };
 }
 
@@ -80,6 +80,7 @@
 
   count: number = 0;
   searchTerm: string;
+  spinnerActive: boolean
   showMenuPromo: boolean;
   private itemsSelected_: boolean = false;
 
@@ -89,7 +90,7 @@
   }
 
   get searchField(): CrToolbarSearchFieldElement {
-    return this.$['main-toolbar'].getSearchField();
+    return this.$.mainToolbar.getSearchField();
   }
 
   deleteSelectedItems() {
diff --git a/chrome/browser/resources/history/router.ts b/chrome/browser/resources/history/router.ts
index bf9e6a69..01cfde5 100644
--- a/chrome/browser/resources/history/router.ts
+++ b/chrome/browser/resources/history/router.ts
@@ -142,6 +142,16 @@
     this.debouncer_ = Debouncer.debounce(
         this.debouncer_, microTask, this.parseUrl_.bind(this));
   }
+
+  getDebouncerForTesting(): Debouncer|null {
+    return this.debouncer_;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-router': HistoryRouterElement;
+  }
 }
 
 customElements.define(HistoryRouterElement.is, HistoryRouterElement);
diff --git a/chrome/browser/resources/history/searched_label.ts b/chrome/browser/resources/history/searched_label.ts
index 1925826c2..d9e18b6 100644
--- a/chrome/browser/resources/history/searched_label.ts
+++ b/chrome/browser/resources/history/searched_label.ts
@@ -68,5 +68,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-searched-label': HistorySearchedLabelElement;
+  }
+}
+
 customElements.define(
     HistorySearchedLabelElement.is, HistorySearchedLabelElement);
diff --git a/chrome/browser/resources/history/side_bar.html b/chrome/browser/resources/history/side_bar.html
index 2008f1be..c77a1ce 100644
--- a/chrome/browser/resources/history/side_bar.html
+++ b/chrome/browser/resources/history/side_bar.html
@@ -122,7 +122,7 @@
           on-iron-activate="onSelectorActivate_" selected-attribute="selected">
         <a id="history" role="menuitem" class="page-item cr-nav-menu-item"
             href="[[getHistoryItemHref_(selectedTab, showHistoryClusters_)]]"
-            path="[[getHistoryItemPath_(selectedTab, showHistoryClusters_)]]"
+            path$="[[getHistoryItemPath_(selectedTab, showHistoryClusters_)]]"
             on-click="onItemClick_">
           <iron-icon icon="cr:schedule"></iron-icon>
           $i18n{historyMenuItem}
diff --git a/chrome/browser/resources/history/side_bar.ts b/chrome/browser/resources/history/side_bar.ts
index bd923a17..82ff352 100644
--- a/chrome/browser/resources/history/side_bar.ts
+++ b/chrome/browser/resources/history/side_bar.ts
@@ -17,6 +17,7 @@
 import './strings.m.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 import {PaperRippleElement} from 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -34,7 +35,11 @@
 export interface HistorySideBarElement {
   $: {
     'cbd-ripple': PaperRippleElement,
+    'history': HTMLAnchorElement,
+    'menu': IronSelectorElement,
     'thc-ripple': PaperRippleElement,
+    'toggle-history-clusters': HTMLElement,
+    'syncedTabs': HTMLElement,
   };
 }
 
diff --git a/chrome/browser/resources/history/synced_device_card.ts b/chrome/browser/resources/history/synced_device_card.ts
index b0552287..000b6273f 100644
--- a/chrome/browser/resources/history/synced_device_card.ts
+++ b/chrome/browser/resources/history/synced_device_card.ts
@@ -36,6 +36,7 @@
   $: {
     'card-heading': HTMLDivElement,
     'collapse': IronCollapseElement,
+    'menu-button': HTMLElement,
   };
 }
 
@@ -193,5 +194,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-synced-device-card': HistorySyncedDeviceCardElement;
+  }
+}
+
 customElements.define(
     HistorySyncedDeviceCardElement.is, HistorySyncedDeviceCardElement);
diff --git a/chrome/browser/resources/history/synced_device_manager.ts b/chrome/browser/resources/history/synced_device_manager.ts
index c916fd4..cf631f9 100644
--- a/chrome/browser/resources/history/synced_device_manager.ts
+++ b/chrome/browser/resources/history/synced_device_manager.ts
@@ -345,5 +345,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-synced-device-manager': HistorySyncedDeviceManagerElement;
+  }
+}
+
 customElements.define(
     HistorySyncedDeviceManagerElement.is, HistorySyncedDeviceManagerElement);
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 9a0c917..c1327805 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -191,6 +191,7 @@
 ts_library("build_ts") {
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
+  composite = true
   tsconfig_base = "tsconfig_base.json"
   in_files = web_component_files + non_web_component_files + mojo_js_files
 
diff --git a/chrome/browser/resources/ntp4/apps_page.js b/chrome/browser/resources/ntp4/apps_page.js
index 26846e6..2f09555 100644
--- a/chrome/browser/resources/ntp4/apps_page.js
+++ b/chrome/browser/resources/ntp4/apps_page.js
@@ -203,6 +203,12 @@
     this.uninstall_.disabled = !app.appData.mayDisable;
     if (this.appinfo_) {
       this.appinfo_.hidden = !app.appData.isLocallyInstalled;
+
+      if (app.appData.settingsMenuItemOverrideText) {
+        this.appinfo_.textContent = app.appData.settingsMenuItemOverrideText;
+      } else {
+        this.appinfo_.textContent = loadTimeData.getString('appinfodialog');
+      }
     }
 
     this.createShortcutSeparator_.hidden = this.createShortcut_.hidden =
@@ -216,11 +222,7 @@
     this.runOnOsLogin_.checked =
         app.appData.runOnOsLoginMode != RUN_ON_OS_LOGIN_MODE.NOT_RUN;
 
-    if (app.appData.settingsMenuItemOverrideText) {
-      this.appinfo_.textContent = app.appData.settingsMenuItemOverrideText;
-    } else {
-      this.appinfo_.textContent = loadTimeData.getString('appinfodialog');
-    }
+
   },
 
   /** @private */
diff --git a/chrome/browser/resources/read_later/side_panel/reader_mode/app.ts b/chrome/browser/resources/read_later/side_panel/reader_mode/app.ts
index 9aff43ac..083103f 100644
--- a/chrome/browser/resources/read_later/side_panel/reader_mode/app.ts
+++ b/chrome/browser/resources/read_later/side_panel/reader_mode/app.ts
@@ -35,6 +35,7 @@
   private apiProxy_: ReaderModeApiProxy = ReaderModeApiProxy.getInstance();
   private readLaterApi_: ReadLaterApiProxy =
       ReadLaterApiProxyImpl.getInstance();
+  private listenerIds_: number[];
   private paragraphs_: string[];
 
   connectedCallback() {
@@ -43,12 +44,24 @@
       // Show the UI as soon as the app is connected.
       this.readLaterApi_.showUI();
     }
-    this.showReaderMode_();
+
+    const callbackRouter = this.apiProxy_.getCallbackRouter();
+    this.listenerIds_ = [callbackRouter.onEssentialContent.addListener(
+        (essential_content: string[]) =>
+            this.showEssentialContent_(essential_content))];
+
+    this.apiProxy_.showUI();
   }
 
-  async showReaderMode_() {
-    const {result} = await this.apiProxy_.showReaderMode();
-    this.paragraphs_ = result;
+  disconnectedCallback() {
+    super.disconnectedCallback();
+
+    this.listenerIds_.forEach(
+        id => this.apiProxy_.getCallbackRouter().removeListener(id));
+  }
+
+  showEssentialContent_(essential_content: string[]) {
+    this.paragraphs_ = essential_content;
   }
 }
 customElements.define(ReaderModeElement.is, ReaderModeElement);
diff --git a/chrome/browser/resources/read_later/side_panel/reader_mode/reader_mode_api_proxy.ts b/chrome/browser/resources/read_later/side_panel/reader_mode/reader_mode_api_proxy.ts
index 1ba9cec..0cac321f 100644
--- a/chrome/browser/resources/read_later/side_panel/reader_mode/reader_mode_api_proxy.ts
+++ b/chrome/browser/resources/read_later/side_panel/reader_mode/reader_mode_api_proxy.ts
@@ -2,22 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageHandlerFactory, PageHandlerRemote} from './reader_mode.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './reader_mode.mojom-webui.js';
 
 let instance: ReaderModeApiProxy|null = null;
 
 export class ReaderModeApiProxy {
+  callbackRouter: PageCallbackRouter;
   handler: PageHandlerRemote;
 
   constructor() {
+    this.callbackRouter = new PageCallbackRouter();
     this.handler = new PageHandlerRemote();
-
     const factory = PageHandlerFactory.getRemote();
-    factory.createPageHandler(this.handler.$.bindNewPipeAndPassReceiver());
+    factory.createPageHandler(
+        this.callbackRouter.$.bindNewPipeAndPassRemote(),
+        this.handler.$.bindNewPipeAndPassReceiver());
   }
 
-  showReaderMode() {
-    return this.handler.showReaderMode();
+  getCallbackRouter() {
+    return this.callbackRouter;
+  }
+
+  showUI() {
+    this.handler.showUI();
   }
 
   static getInstance() {
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.ts b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
index ac2d904..bfca7b4 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.ts
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.ts
@@ -203,7 +203,11 @@
 
   currentRouteChanged(route: Route) {
     if (document.documentElement.hasAttribute('enable-branding-update')) {
-      if (route.depth <= 1) {
+      if (route === routes.PRIVACY_REVIEW) {
+        // Privacy Review has a multi-card layout, which only needs shadows to
+        // show when there is more content to scroll.
+        this.enableShadowBehavior(true);
+      } else if (route.depth <= 1) {
         // Main page uses scroll position to determine whether a shadow should
         // be shown.
         this.enableShadowBehavior(true);
diff --git a/chrome/browser/resources/tab_search/alert_indicators/tab_audio_muting_rounded.svg b/chrome/browser/resources/tab_search/alert_indicators/tab_audio_muting_rounded.svg
index 3da0244c..348df23e 100644
--- a/chrome/browser/resources/tab_search/alert_indicators/tab_audio_muting_rounded.svg
+++ b/chrome/browser/resources/tab_search/alert_indicators/tab_audio_muting_rounded.svg
@@ -1 +1,3 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M 8.5 6 C 8.5 5.02 7.93 4.17 7.11 3.76 L 7.11 4.99 L 8.47 6.35 C 8.49 6.24 8.5 6.12 8.5 6 Z M 9.89 6 C 9.89 6.52 9.78 7.01 9.59 7.47 L 10.43 8.31 C 10.79 7.62 11 6.83 11 6 C 11 3.62 9.34 1.63 7.11 1.13 L 7.11 2.27 C 8.72 2.75 9.89 4.24 9.89 6 Z M 1.71 1 L 1 1.71 L 3.63 4.33 L 1 4.33 L 1 7.67 L 3.22 7.67 L 6 10.44 L 6 6.71 L 8.36 9.07 C 7.99 9.36 7.57 9.58 7.11 9.72 L 7.11 10.87 C 7.88 10.69 8.57 10.34 9.16 9.86 L 10.29 11 L 11 10.29 L 6 5.29 L 1.71 1 Z M 6 1.56 L 4.84 2.72 L 6 3.88 L 6 1.56 Z" /></svg>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2.84667 2L2 2.84667L5.15333 6H2V10H4.66667L8 13.3333V8.84667L10.8333 11.68C10.3867 12.0267 9.88667 12.3 9.33333 12.4667V13.84C10.2533 13.6333 11.0867 13.2067 11.7933 12.6333L13.1533 14L14 13.1533L8 7.15333L2.84667 2ZM12.3067 9.76C12.5333 9.21333 12.6667 8.62667 12.6667 8C12.6667 5.88667 11.26 4.1 9.33333 3.52667V2.15333C12.0067 2.76 14 5.14667 14 8C14 9 13.7533 9.94 13.3133 10.7667L12.3067 9.76ZM9.33333 5.31333C10.32 5.80667 11 6.82 11 8C11 8.14667 10.9867 8.28667 10.9667 8.42L9.33333 6.78667V5.31333ZM6.60667 4.06L8 2.66667V5.45333L6.60667 4.06Z" fill="black"/>
+</svg>
diff --git a/chrome/browser/resources/tab_search/alert_indicators/tab_audio_rounded.svg b/chrome/browser/resources/tab_search/alert_indicators/tab_audio_rounded.svg
index a09790ea..74d8445 100644
--- a/chrome/browser/resources/tab_search/alert_indicators/tab_audio_rounded.svg
+++ b/chrome/browser/resources/tab_search/alert_indicators/tab_audio_rounded.svg
@@ -1 +1,9 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M 1 4.29 L 1 7.71 L 3.22 7.71 L 6 10.56 L 6 1.44 L 3.22 4.29 L 1 4.29 Z M 8.5 6 C 8.5 4.99 7.93 4.12 7.11 3.7 L 7.11 8.29 C 7.93 7.88 8.5 7.01 8.5 6 Z M 7.11 1 L 7.11 2.17 C 8.72 2.66 9.89 4.19 9.89 6 C 9.89 7.81 8.72 9.34 7.11 9.83 L 7.11 11 C 9.34 10.48 11 8.44 11 6 C 11 3.56 9.34 1.52 7.11 1 Z" /></svg>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.33333 3.40935V2C12.0067 2.62258 14 5.07184 14 8C14 10.9282 12.0067 13.3774 9.33333 14V12.5906C11.26 12.0023 12.6667 10.1688 12.6667 8C12.6667 5.83124 11.26 3.99772 9.33333 3.40935ZM2 5.94755V10.0525H4.66667L8 13.4732V2.5268L4.66667 5.94755H2ZM11 8C11 6.78905 10.32 5.74914 9.33333 5.24287V10.7503C10.32 10.2509 11 9.21095 11 8Z" fill="black"/>
+<mask id="mask0_2440_57228" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="2" width="12" height="12">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.33333 3.40935V2C12.0067 2.62258 14 5.07184 14 8C14 10.9282 12.0067 13.3774 9.33333 14V12.5906C11.26 12.0023 12.6667 10.1688 12.6667 8C12.6667 5.83124 11.26 3.99772 9.33333 3.40935ZM2 5.94755V10.0525H4.66667L8 13.4732V2.5268L4.66667 5.94755H2ZM11 8C11 6.78905 10.32 5.74914 9.33333 5.24287V10.7503C10.32 10.2509 11 9.21095 11 8Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_2440_57228)">
+<rect width="16" height="16" fill="black"/>
+</g>
+</svg>
diff --git a/chrome/browser/ssl/known_interception_disclosure_infobar_browsertest.cc b/chrome/browser/ssl/known_interception_disclosure_infobar_browsertest.cc
index 0e157389..0d63d0b 100644
--- a/chrome/browser/ssl/known_interception_disclosure_infobar_browsertest.cc
+++ b/chrome/browser/ssl/known_interception_disclosure_infobar_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/files/file_util.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/known_interception_disclosure_infobar_delegate.h"
 #include "chrome/browser/ui/browser.h"
@@ -152,8 +153,15 @@
   EXPECT_EQ(0u, GetInfobarCount(tab));
 }
 
+#if defined(IS_CHROMEOS_LACROS)
+#define MAYBE_CooldownResetsOnBrowserRestartDesktop \
+  DISABLED_CooldownResetsOnBrowserRestartDesktop
+#else
+#define MAYBE_CooldownResetsOnBrowserRestartDesktop \
+  CooldownResetsOnBrowserRestartDesktop
+#endif
 IN_PROC_BROWSER_TEST_F(KnownInterceptionDisclosureInfobarTest,
-                       CooldownResetsOnBrowserRestartDesktop) {
+                       MAYBE_CooldownResetsOnBrowserRestartDesktop) {
   const GURL kInterceptedUrl(https_server_.GetURL("/ssl/google.html"));
 
   // On restart, no infobar should be shown initially.
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 2441997b..38423ca 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -821,12 +821,6 @@
       <message name="IDS_CONTEXTUAL_SEARCH_NO_THANKS_BUTTON" desc="A button to confirm and dismiss opt out promo">
         No thanks
       </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_HELP_BODY" desc="Some help text that is shown in the body of a paragraph. The text tells the user what gesture to use instead of a previous gesture in order to search.">
-        To search from this page, touch &amp; hold words instead of tapping them
-      </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_HELP_HEADER" desc="Summary help text that is shown at the top of a paragraph. The text tells the user what gesture to use in order to search.">
-        Touch &amp; hold for more relevant info
-      </message>
       <message name="IDS_CONTEXTUAL_SEARCH_DEFAULT_CAPTION" desc="Summary help text that is shown as a second line in the Contextual Search peeking bottom sheet telling the user that they can tap the text in order to open the sheet and see search results there.">
         Tap to see search results
       </message>
@@ -3893,24 +3887,9 @@
       <message name="IDS_CONTEXTUAL_SEARCH_QUICK_ACTION_CAPTION_GENERIC_WEBSITE" desc="Caption displayed in the Contextual Search bar prompting the user to navigate to a web page.">
         Go to page
       </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_IPH_ENABLE" desc="An in-product-help message for the Touch to Search feature on Chrome Android. The message encourages users to let Google Search servers have access to the page content to get better translations.">
-        To get better translations, let Google Search use the current page
-      </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_IPH_ENTITY" desc="An in-product-help message for the Tap to Search feature on Chrome Android. The message encourages users to open a panel that contains search results for the word or phrase they tapped on.">
-        See instant search results in this panel
-      </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_IPH_SEARCH_RESULT" desc="An in-product-help message for the Tap to Search feature. It encourages users to open the Tap to Search panel to see search results for a word or phrase they have tapped.">
-        Tap a word to search instantly or see related actions
-      </message>
       <message name="IDS_CONTEXTUAL_SEARCH_IPH_TAP" desc="An in-product-help message for encouraging users to tap instead of long pressing to trigger the Tap to Search feature.">
         You can also search with a quick tap on a word
       </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD" desc="An in-product-help message for encouraging users to use a touch and hold gesture to trigger the Touch to Search feature.">
-        To search, touch &amp; hold a word
-      </message>
-      <message name="IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD_ENGAGED" desc="An in-product-help message for encouraging engaged users to use a touch and hold gesture to trigger the Touch to Search feature.">
-        To search, touch &amp; hold a word instead of tapping it
-      </message>
 
       <!-- Web apps -->
       <message name="IDS_WEBAPP_ACTIVITY_TITLE" desc="Title in recent tasks list for web apps, i.e. web pages that are shown in a separate window">
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_BODY.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_BODY.png.sha1
deleted file mode 100644
index 1ca9ff4..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_BODY.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bf6b307b964bb85b3d512557af2437cafbab66c7
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_HEADER.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_HEADER.png.sha1
deleted file mode 100644
index 1ca9ff4..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_HELP_HEADER.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bf6b307b964bb85b3d512557af2437cafbab66c7
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_ENABLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_ENABLE.png.sha1
deleted file mode 100644
index 292ead2..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_ENABLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-08067fe02c6a052825d055566f5cc5053af80552
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD.png.sha1
deleted file mode 100644
index b10bab3..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fda99aefdc968216717637b67d10f0725ec0a1e6
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD_ENGAGED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD_ENGAGED.png.sha1
deleted file mode 100644
index 872fc3f..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CONTEXTUAL_SEARCH_IPH_TOUCH_AND_HOLD_ENGAGED.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-55b924ffb789616445adebd4fec6a843bbfe9efd
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/app_context_menu.cc b/chrome/browser/ui/app_list/app_context_menu.cc
index d0bfea92..6d7816a 100644
--- a/chrome/browser/ui/app_list/app_context_menu.cc
+++ b/chrome/browser/ui/app_list/app_context_menu.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/grit/generated_resources.h"
@@ -77,7 +78,7 @@
       GetMenuItemVectorIcon(command_id, controller_->IsAppPinned(app_id_)
                                             ? IDS_APP_LIST_CONTEXT_MENU_UNPIN
                                             : IDS_APP_LIST_CONTEXT_MENU_PIN);
-  return ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+  return ui::ImageModel::FromVectorIcon(icon, apps::GetColorIdForMenuItemIcon(),
                                         ash::kAppContextMenuIconSize);
 }
 
@@ -166,7 +167,7 @@
   if (!icon.is_empty()) {
     menu_model->AddItemWithStringIdAndIcon(
         command_id, string_id,
-        ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+        ui::ImageModel::FromVectorIcon(icon, apps::GetColorIdForMenuItemIcon(),
                                        ash::kAppContextMenuIconSize));
     return;
   }
diff --git a/chrome/browser/ui/app_list/app_list_sort_unittest.cc b/chrome/browser/ui/app_list/app_list_sort_unittest.cc
index b313d544..4751905803 100644
--- a/chrome/browser/ui/app_list/app_list_sort_unittest.cc
+++ b/chrome/browser/ui/app_list/app_list_sort_unittest.cc
@@ -584,8 +584,7 @@
 
   // Move an from the folder to root apps grid.
   ChromeAppListModelUpdater* model_updater = GetChromeModelUpdater();
-  model_updater->RequestMoveItemToFolder(
-      apps[7]->id(), kFolderItemId, ash::RequestMoveToFolderReason::kMoveItem);
+  model_updater->RequestMoveItemToFolder(apps[7]->id(), kFolderItemId);
 
   // Verify that the app list is still considered sorted - new items are
   // added to the app list to maintain sorted order.
@@ -815,18 +814,87 @@
   syncer::StringOrdinal position =
       model_updater->FindItem(kItemId4)->position().CreateBefore();
   const std::string kFolderItemId = GenerateId("folder_id1");
-  std::unique_ptr<ChromeAppListItem> folder_item =
-      std::make_unique<ChromeAppListItem>(profile_.get(), kFolderItemId,
-                                          model_updater);
-  folder_item->SetChromeIsFolder(true);
-  ChromeAppListItem::TestApi(folder_item.get()).SetPosition(position);
-  ChromeAppListItem::TestApi(folder_item.get()).SetName("Folder1");
-  app_list_syncable_service()->AddItem(std::move(folder_item));
-  model_updater->RequestMoveItemToFolder(
-      kItemId4, kFolderItemId, ash::RequestMoveToFolderReason::kMergeFirstItem);
-  model_updater->RequestMoveItemToFolder(
-      kItemId3, kFolderItemId,
-      ash::RequestMoveToFolderReason::kMergeSecondItem);
+  const std::string folder_item_id =
+      model_updater->model_for_test()->MergeItems(kItemId4, kItemId3);
+  model_updater->RequestFolderRename(folder_item_id, "Folder1");
+
+  // Verify that:
+  // (1) Temporary sort ends.
+  // (2) Sort order is cleared.
+  // (3) Local positions are committed.
+  EXPECT_FALSE(IsUnderTemporarySort());
+  EXPECT_EQ(ash::AppListSortOrder::kCustom, GetSortOrderFromPrefs());
+  EXPECT_EQ(GetOrderedItemIdsFromSyncableService(),
+            std::vector<std::string>(
+                {folder_item_id, kItemId4, kItemId3, kItemId2, kItemId1}));
+  EXPECT_EQ(GetOrderedItemIdsFromModelUpdater(),
+            std::vector<std::string>(
+                {folder_item_id, kItemId4, kItemId3, kItemId2, kItemId1}));
+}
+
+// Verifies that the app list under temporary sort works as expected when a
+// folder gets renamed.
+TEST_F(TemporaryAppListSortTest, HandleFolderRename) {
+  RemoveAllExistingItems();
+
+  // Configure sunc data with a folder containing two apps.
+  const std::string kFolderItemId = "folder_id";
+  syncer::SyncDataList sync_list;
+  sync_list.push_back(CreateAppRemoteData(
+      kFolderItemId, "Folder", "",
+      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue(), kUnset,
+      sync_pb::AppListSpecifics_AppListItemType_TYPE_FOLDER));
+  const std::string kItemId1 = GenerateId("app_id1");
+  const std::string kItemId2 = GenerateId("app_id2");
+
+  syncer::StringOrdinal child_position =
+      syncer::StringOrdinal::CreateInitialOrdinal();
+  sync_list.push_back(CreateAppRemoteData(
+      kItemId1, "A", kFolderItemId, child_position.ToInternalValue(), kUnset));
+  child_position = child_position.CreateAfter();
+  sync_list.push_back(CreateAppRemoteData(
+      kItemId2, "B", kFolderItemId, child_position.ToInternalValue(), kUnset));
+  app_list_syncable_service()->MergeDataAndStartSyncing(
+      syncer::APP_LIST, sync_list,
+      std::make_unique<syncer::FakeSyncChangeProcessor>(),
+      std::make_unique<syncer::SyncErrorFactoryMock>());
+  content::RunAllTasksUntilIdle();
+
+  // Install four apps.
+  scoped_refptr<extensions::Extension> app1 =
+      MakeApp("A", kItemId1, extensions::Extension::NO_FLAGS);
+  InstallExtension(app1.get());
+
+  scoped_refptr<extensions::Extension> app2 =
+      MakeApp("B", kItemId2, extensions::Extension::NO_FLAGS);
+  InstallExtension(app2.get());
+
+  const std::string kItemId3 = CreateNextAppId(GenerateId("app_id3"));
+  scoped_refptr<extensions::Extension> app3 =
+      MakeApp("C", kItemId3, extensions::Extension::NO_FLAGS);
+  InstallExtension(app3.get());
+
+  const std::string kItemId4 = CreateNextAppId(GenerateId("app_id4"));
+  scoped_refptr<extensions::Extension> app4 =
+      MakeApp("D", kItemId4, extensions::Extension::NO_FLAGS);
+  InstallExtension(app4.get());
+
+  // Sort with the name alphabetical order.
+  ChromeAppListModelUpdater* model_updater = GetChromeModelUpdater();
+  model_updater->RequestAppListSort(ash::AppListSortOrder::kNameAlphabetical);
+  Commit();
+
+  // Sort with the name reverse alphabetical order without committing. The
+  // permanent sort order and the permanent item positions should not change.
+  model_updater->RequestAppListSort(
+      ash::AppListSortOrder::kNameReverseAlphabetical);
+  EXPECT_EQ(GetOrderedItemIdsFromSyncableService(),
+            std::vector<std::string>(
+                {kItemId1, kItemId2, kItemId3, kItemId4, kFolderItemId}));
+  EXPECT_EQ(ash::AppListSortOrder::kNameAlphabetical, GetSortOrderFromPrefs());
+
+  // Rename the test folder.
+  model_updater->RequestFolderRename(kFolderItemId, "A new folder name");
 
   // Verify that:
   // (1) Temporary sort ends.
@@ -914,8 +982,7 @@
             GetOrderedNamesFromSyncableService());
 
   // Move `app3` to the folder.
-  model_updater->RequestMoveItemToFolder(
-      kItemId3, kFolderItemId, ash::RequestMoveToFolderReason::kMoveItem);
+  model_updater->RequestMoveItemToFolder(kItemId3, kFolderItemId);
 
   // Verify that:
   // (1) Temporary sort ends.
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 10097b4d..6a594ee 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -1364,6 +1364,15 @@
   return reorder::CalculateFrontPosition(sync_items_);
 }
 
+bool AppListSyncableService::CalculateNewItemPosition(
+    const ChromeAppListItem& new_item,
+    const std::vector<const ChromeAppListItem*>& local_items,
+    syncer::StringOrdinal* target_position) const {
+  return reorder::CalculateNewItemPosition(GetPermanentSortingOrder(), new_item,
+                                           local_items, &sync_items_,
+                                           target_position);
+}
+
 ash::AppListSortOrder AppListSyncableService::GetPermanentSortingOrder() const {
   return static_cast<ash::AppListSortOrder>(
       profile()->GetPrefs()->GetInteger(prefs::kAppListPreferredOrder));
@@ -1777,12 +1786,10 @@
   // The target position of `new_item`.
   syncer::StringOrdinal position;
 
-  ash::AppListSortOrder order = GetPermanentSortingOrder();
-
   // TODO(https://crbug.com/1260877): ideally we would not have to create a
   // one-off vector of items using `GetItems()`.
-  bool is_successful = reorder::CalculateNewItemPosition(
-      order, *new_item, model_updater_->GetItems(), &sync_items_, &position);
+  bool is_successful = CalculateNewItemPosition(
+      *new_item, model_updater_->GetItems(), &position);
 
   // If `new_item` cannot be placed following the specified order, `new_item`
   // should be placed at front. Also reset the sorting order.
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h
index 9004af3..95035b50 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.h
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.h
@@ -240,6 +240,10 @@
   // reorder::AppListReorderDelegate:
   void SetAppListPreferredOrder(ash::AppListSortOrder order) override;
   syncer::StringOrdinal CalculateGlobalFrontPosition() const override;
+  bool CalculateNewItemPosition(
+      const ChromeAppListItem& new_item,
+      const std::vector<const ChromeAppListItem*>& local_items,
+      syncer::StringOrdinal* target_position) const override;
   ash::AppListSortOrder GetPermanentSortingOrder() const override;
 
  private:
diff --git a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
index 6077b8f..5acebb10 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
@@ -391,25 +391,31 @@
 
   if (add_sort_options_) {
     reorder_submenu_ = std::make_unique<ui::SimpleMenuModel>(this);
+    const ui::ColorId color_id = apps::GetColorIdForMenuItemIcon();
     // As all the options below are only for tests and are expected to change in
     // the future, the strings are directly written as the parameters.
     reorder_submenu_->AddItemWithIcon(
         ash::REORDER_BY_NAME_ALPHABETICAL,
         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_BY_NAME),
-        ui::ImageModel::FromVectorIcon(GetMenuItemVectorIcon(
-            ash::REORDER_BY_NAME_ALPHABETICAL, /*string_id=*/-1)));
+        ui::ImageModel::FromVectorIcon(
+            GetMenuItemVectorIcon(ash::REORDER_BY_NAME_ALPHABETICAL,
+                                  /*string_id=*/-1),
+            color_id));
     reorder_submenu_->AddItemWithIcon(
         ash::REORDER_BY_COLOR,
         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_BY_COLOR),
         ui::ImageModel::FromVectorIcon(
-            GetMenuItemVectorIcon(ash::REORDER_BY_COLOR, /*string_id=*/-1)));
+            GetMenuItemVectorIcon(ash::REORDER_BY_COLOR, /*string_id=*/-1),
+            color_id));
     menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+
     menu_model->AddSubMenuWithIcon(
         ash::REORDER_SUBMENU,
         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_TITLE),
         reorder_submenu_.get(),
         ui::ImageModel::FromVectorIcon(
-            GetMenuItemVectorIcon(ash::REORDER_SUBMENU, /*string_id=*/-1)));
+            GetMenuItemVectorIcon(ash::REORDER_SUBMENU, /*string_id=*/-1),
+            color_id));
   }
 
   std::move(callback).Run(std::move(menu_model));
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index 908d8c4b..57df676 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -733,8 +733,7 @@
 
 void ChromeAppListModelUpdater::RequestMoveItemToFolder(
     std::string id,
-    const std::string& folder_id,
-    ash::RequestMoveToFolderReason reason) {
+    const std::string& folder_id) {
   DCHECK(!folder_id.empty());
 
   ash::AppListItem* item = model_.FindItem(id);
@@ -766,14 +765,9 @@
     } else {
       ChromeAppListItem* last_child =
           item_manager_->FindLastChildInFolder(folder_id);
-      if (!last_child) {
-        // The moved item is the first item under folder.
-        target_position = syncer::StringOrdinal::CreateInitialOrdinal();
-      } else {
-        // TODO(https://crbug.com/1247408): now the new item is always added to
-        // the rear. We should take launcher sort order into consideration.
-        target_position = last_child->position().CreateAfter();
-      }
+      target_position = last_child
+                            ? last_child->position().CreateAfter()
+                            : syncer::StringOrdinal::CreateInitialOrdinal();
     }
 
     const std::string old_folder_id = item->folder_id();
@@ -791,33 +785,13 @@
   // When user moves a local item to a folder, the user is believed to accept
   // the item layout after reordering. Therefore local positions are
   // committed.
-  if (reason == ash::RequestMoveToFolderReason::kMergeSecondItem) {
-    // Clear the sort order. Note that the folder that is created by merging
-    // may not be placed following the temporary sort order. Therefore the
-    // sort order is cleared.
-    if (is_under_temporary_sort()) {
-      EndTemporarySortAndTakeAction(EndAction::kCommitAndClearSort);
-    } else {
-      if (order_delegate_) {
-        order_delegate_->SetAppListPreferredOrder(
-            ash::AppListSortOrder::kCustom);
-      }
-      // NOTE: Committing temporary sort will also reset page breaks, so they
-      // don't have to be sanitized again in that case.
-      sync_model_sanitizer_->SanitizePageBreaksForProductivityLauncher(
-          GetTopLevelItemIds(), /*reset_page_breaks=*/false);
-    }
-  } else if (reason == ash::RequestMoveToFolderReason::kMoveItem) {
-    // When an item is moved to an existing folder, the sorting order is still
-    // maintained. Therefore commit the temporary order in this scenario.
-    if (is_under_temporary_sort()) {
-      EndTemporarySortAndTakeAction(EndAction::kCommit);
-    } else {
-      // NOTE: Committing temporary sort will also reset page breaks, so they
-      // don't have to be sanitized again in that case.
-      sync_model_sanitizer_->SanitizePageBreaksForProductivityLauncher(
-          GetTopLevelItemIds(), /*reset_page_breaks=*/false);
-    }
+  if (is_under_temporary_sort()) {
+    EndTemporarySortAndTakeAction(EndAction::kCommit);
+  } else {
+    // NOTE: Committing temporary sort will also reset page breaks, so they
+    // don't have to be sanitized again in that case.
+    sync_model_sanitizer_->SanitizePageBreaksForProductivityLauncher(
+        GetTopLevelItemIds(), /*reset_page_breaks=*/false);
   }
 }
 
@@ -924,6 +898,145 @@
   }
 }
 
+std::string ChromeAppListModelUpdater::RequestFolderCreation(
+    std::string merge_target_id,
+    std::string item_to_merge_id) {
+  bool sort_order_invalidated =
+      !ash::features::IsLauncherFolderRenameKeepsSortOrderEnabled();
+  // Folder creation is a user action, so temporary sort state should end.
+  // If feature to position the folder to correct sorted position is disabled,
+  // clear the sort.
+  if (is_under_temporary_sort()) {
+    EndTemporarySortAndTakeAction(sort_order_invalidated
+                                      ? EndAction::kCommitAndClearSort
+                                      : EndAction::kCommit);
+  }
+
+  ash::AppListItem* target_item = model_.FindItem(merge_target_id);
+  DCHECK(target_item);
+  DCHECK(!target_item->is_folder());
+  DCHECK_EQ("", target_item->folder_id());
+
+  ash::AppListItem* item_to_merge = model_.FindItem(item_to_merge_id);
+  DCHECK(item_to_merge);
+  DCHECK(!item_to_merge->is_folder());
+
+  ash::AppListSortOrder current_sort_order = ash::AppListSortOrder::kCustom;
+  if (ash::features::IsLauncherAppSortEnabled()) {
+    if (sort_order_invalidated) {
+      order_delegate_->SetAppListPreferredOrder(ash::AppListSortOrder::kCustom);
+    } else {
+      current_sort_order = order_delegate_->GetPermanentSortingOrder();
+    }
+  }
+
+  // Create a new folder.
+  const std::string new_folder_id = ash::AppListFolderItem::GenerateId();
+  std::unique_ptr<ChromeAppListItem> new_folder_item =
+      std::make_unique<ChromeAppListItem>(profile_, new_folder_id, this);
+  new_folder_item->SetChromeIsFolder(true);
+
+  // Calculate the new folder's sorted position - if apps grid is not sorted,
+  // default to the original item position.
+  syncer::StringOrdinal target_position = target_item->position();
+  if (current_sort_order != ash::AppListSortOrder::kCustom) {
+    syncer::StringOrdinal sorted_position;
+    bool has_sorted_position = order_delegate_->CalculateNewItemPosition(
+        *new_folder_item, GetItems(), &sorted_position);
+    if (has_sorted_position)
+      target_position = sorted_position;
+  }
+  new_folder_item->SetChromePosition(target_position);
+
+  ChromeAppListItem* chrome_item =
+      item_manager_->AddChromeItem(std::move(new_folder_item));
+  model_.AddItem(CreateAppListItem(chrome_item->CloneMetadata(), this));
+
+  // Adjust parent and position of the item getting mergrd into the target item.
+  const std::string old_folder_id = item_to_merge->folder_id();
+  std::unique_ptr<ash::AppListItemMetadata> item_to_merge_data =
+      item_to_merge->CloneMetadata();
+  item_to_merge_data->folder_id = new_folder_id;
+
+  // When sort is enabled, the item positing relative to `target_item` should
+  // already be correct, otherwise move the item at the end of the folder.
+  if (current_sort_order == ash::AppListSortOrder::kCustom)
+    item_to_merge_data->position = target_item->position().CreateAfter();
+  model_.SetItemMetadata(item_to_merge_id, std::move(item_to_merge_data));
+
+  // If the item was removed from a folder, remove the folder as needed.
+  // Note that empty folder will get removed by the app list model itself.
+  if (!old_folder_id.empty() &&
+      !ash::features::IsProductivityLauncherEnabled()) {
+    DCHECK_EQ(ash::AppListSortOrder::kCustom, current_sort_order);
+    ClearFolderIfItHasSingleChild(old_folder_id);
+  }
+
+  // Set the target item new folder ID.
+  std::unique_ptr<ash::AppListItemMetadata> target_data =
+      target_item->CloneMetadata();
+  target_data->folder_id = new_folder_id;
+  model_.SetItemMetadata(merge_target_id, std::move(target_data));
+
+  sync_model_sanitizer_->SanitizePageBreaksForProductivityLauncher(
+      GetTopLevelItemIds(), /*reset_page_breaks=*/false);
+  return new_folder_id;
+}
+
+void ChromeAppListModelUpdater::RequestFolderRename(
+    std::string folder_id,
+    const std::string& new_name) {
+  ChromeAppListItem* folder_item = FindItem(folder_id);
+  if (!folder_item)
+    return;
+
+  ash::AppListSortOrder current_sort_order = ash::AppListSortOrder::kCustom;
+  if (ash::features::IsLauncherAppSortEnabled()) {
+    if (is_under_temporary_sort()) {
+      current_sort_order = temporary_sort_manager_->temporary_order();
+    } else {
+      current_sort_order = order_delegate_->GetPermanentSortingOrder();
+    }
+  }
+
+  const bool is_name_sort =
+      current_sort_order == ash::AppListSortOrder::kNameAlphabetical ||
+      current_sort_order == ash::AppListSortOrder::kNameReverseAlphabetical;
+  const bool sort_order_invalidated =
+      is_name_sort &&
+      !ash::features::IsLauncherFolderRenameKeepsSortOrderEnabled();
+
+  // If user tries to take an action, and rename a folder - commit temporary
+  // sort.
+  if (is_under_temporary_sort()) {
+    EndTemporarySortAndTakeAction(sort_order_invalidated
+                                      ? EndAction::kCommitAndClearSort
+                                      : EndAction::kCommit);
+  }
+
+  folder_item->SetChromeName(new_name);
+
+  bool position_changed = false;
+  // If app list is sorted alphabetically, the folder name change impacts the
+  // folder position within the sorted list.
+  if (is_name_sort && !sort_order_invalidated) {
+    syncer::StringOrdinal sorted_position;
+    position_changed = order_delegate_->CalculateNewItemPosition(
+        *folder_item, GetItems(), &sorted_position);
+    if (position_changed)
+      folder_item->SetChromePosition(sorted_position);
+  }
+
+  model_.SetItemMetadata(folder_id, folder_item->CloneMetadata());
+
+  if (sort_order_invalidated)
+    order_delegate_->SetAppListPreferredOrder(ash::AppListSortOrder::kCustom);
+  if (position_changed) {
+    sync_model_sanitizer_->SanitizePageBreaksForProductivityLauncher(
+        GetTopLevelItemIds(), /*reset_page_breaks=*/false);
+  }
+}
+
 void ChromeAppListModelUpdater::OnAppListHidden() {
   if (!is_under_temporary_sort())
     return;
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index 6612c37..9b7a26d 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -122,10 +122,13 @@
                              const syncer::StringOrdinal& new_position,
                              ash::RequestPositionUpdateReason reason) override;
   void RequestMoveItemToFolder(std::string id,
-                               const std::string& folder_id,
-                               ash::RequestMoveToFolderReason reason) override;
+                               const std::string& folder_id) override;
   void RequestMoveItemToRoot(std::string id,
                              syncer::StringOrdinal target_position) override;
+  std::string RequestFolderCreation(std::string target_merge_id,
+                                    std::string item_to_merge_id) override;
+  void RequestFolderRename(std::string folder_id,
+                           const std::string& new_name) override;
   void RequestAppListSort(ash::AppListSortOrder order) override;
   void RequestAppListSortRevert() override;
 
diff --git a/chrome/browser/ui/app_list/reorder/app_list_reorder_delegate.h b/chrome/browser/ui/app_list/reorder/app_list_reorder_delegate.h
index 4992438..30944973 100644
--- a/chrome/browser/ui/app_list/reorder/app_list_reorder_delegate.h
+++ b/chrome/browser/ui/app_list/reorder/app_list_reorder_delegate.h
@@ -7,6 +7,8 @@
 
 #include "components/sync/model/string_ordinal.h"
 
+class ChromeAppListItem;
+
 namespace ash {
 enum class AppListSortOrder;
 }
@@ -25,6 +27,15 @@
   // Returns the front position among all sync items.
   virtual syncer::StringOrdinal CalculateGlobalFrontPosition() const = 0;
 
+  // Calcuates a position for a `new_item` so the preferred sort order is
+  // preserved. The sort order is returned through `target_position`. Returns
+  // whether the item should be placed in the sort order, in which case
+  // `target_position` gets set.
+  virtual bool CalculateNewItemPosition(
+      const ChromeAppListItem& new_item,
+      const std::vector<const ChromeAppListItem*>& local_items,
+      syncer::StringOrdinal* target_position) const = 0;
+
   // Returns the sorting order that is saved in perf service and gets shared
   // among synced devices.
   virtual ash::AppListSortOrder GetPermanentSortingOrder() const = 0;
diff --git a/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
index 3d59a213..1b38cdbb 100644
--- a/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
@@ -211,7 +211,7 @@
       open_new_submenu_model_.get(),
       ui::ImageModel::FromVectorIcon(
           GetCommandIdVectorIcon(ash::MENU_OPEN_NEW, GetLaunchTypeStringId()),
-          ui::kColorMenuIcon, ash::kAppContextMenuIconSize));
+          ui::kColorAshSystemUIMenuIcon, ash::kAppContextMenuIconSize));
 }
 
 extensions::LaunchType ExtensionShelfContextMenu::GetLaunchType() const {
diff --git a/chrome/browser/ui/ash/shelf/shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/shelf_context_menu.cc
index c05c5732a..71f23a9 100644
--- a/chrome/browser/ui/ash/shelf/shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_context_menu.cc
@@ -292,7 +292,7 @@
   if (!icon.is_empty()) {
     menu_model->AddItemWithStringIdAndIcon(
         type, string_id,
-        ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+        ui::ImageModel::FromVectorIcon(icon, ui::kColorAshSystemUIMenuIcon,
                                        ash::kAppContextMenuIconSize));
     return;
   }
diff --git a/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_context_menu.cc b/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_context_menu.cc
index c8e00e86..7b41e0c 100644
--- a/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_context_menu.cc
@@ -33,9 +33,9 @@
 
 // Create an appropriately sized ImageModel for a menu item icon.
 ui::ImageModel GetMenuItemIcon(const gfx::VectorIcon& icon) {
-  return ui::ImageModel::FromVectorIcon(icon,
-                                        /*color_id=*/ui::kColorMenuIcon,
-                                        ash::kAppContextMenuIconSize);
+  return ui::ImageModel::FromVectorIcon(
+      icon,
+      /*color_id=*/ui::kColorAshSystemUIMenuIcon, ash::kAppContextMenuIconSize);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 8b00521..f156e97 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -824,9 +824,7 @@
          host != chrome::kChromeUIHelpHost &&
          host != chrome::kChromeUIHistoryHost &&
          host != chrome::kChromeUIExtensionsHost &&
-         host != chrome::kChromeUIBookmarksHost &&
-         host != chrome::kChromeUINewTabPageHost &&
-         host != chrome::kChromeUINewTabPageThirdPartyHost;
+         host != chrome::kChromeUIBookmarksHost;
 }
 
 bool IsURLAllowedInIncognito(const GURL& url,
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index efd2a61..4c69fb47 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -73,14 +73,6 @@
   return GURL(chrome::kChromeUISettingsURL);
 }
 
-GURL GetWebUINewTabPage() {
-  return GURL(chrome::kChromeUINewTabPageURL);
-}
-
-GURL GetWebUINewTabPageThirdParty() {
-  return GURL(chrome::kChromeUINewTabPageThirdPartyURL);
-}
-
 GURL GetContentSettingsURL() {
   return GetSettingsURL().Resolve(chrome::kContentSettingsSubPage);
 }
@@ -1289,24 +1281,6 @@
       GetSettingsURL(), ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
 }
 
-// This test verifies that chrome://new-tab-page isn't opened in the incognito
-// window.
-IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
-                       Disposition_WebUINewTabPage_UseNonIncognitoWindow) {
-  RunUseNonIncognitoWindowTest(
-      GetWebUINewTabPage(), ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
-}
-
-// This test verifies that chrome://new-tab-page-third-party isn't opened in the
-// incognito window.
-IN_PROC_BROWSER_TEST_F(
-    BrowserNavigatorTest,
-    Disposition_WebUINewTabPageThirdParty_UseNonIncognitoWindow) {
-  RunUseNonIncognitoWindowTest(
-      GetWebUINewTabPageThirdParty(),
-      ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
-}
-
 // This test verifies that the view-source settings page isn't opened in the
 // incognito window.
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
index bebb41b..817d86bf 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
@@ -116,6 +116,19 @@
   EXPECT_EQ(1u, notification_producer_->GetActiveItemCount());
 }
 
+// The GlobalMediaControlsCastStartStop flag is disabled on ChromeOS.
+#if BUILDFLAG(IS_CHROMEOS)
+TEST_F(CastMediaNotificationProducerTest, RoutesWithoutNotifications) {
+  // These routes should not have notification items created for them.
+  MediaRoute no_controller_route = CreateRoute("route-1");
+  no_controller_route.set_controller_type(RouteControllerType::kNone);
+  MediaRoute multizone_member_route = CreateRoute("route-2", "cast:705D30C6");
+
+  notification_producer_->OnRoutesUpdated(
+      {no_controller_route, multizone_member_route});
+  EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
+}
+#else
 TEST_F(CastMediaNotificationProducerTest, RoutesWithoutNotifications) {
   // These routes should not have notification items created for them.
   MediaRoute mirroring_route =
@@ -128,3 +141,4 @@
       {mirroring_route, multizone_member_route, connecting_route});
   EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
 }
+#endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc b/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
index 6d012bf..1817ca9 100644
--- a/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
@@ -119,8 +119,14 @@
   // A local mirroring route should show the action icon.
   UpdateRoutesAndExpectIconShown({local_mirroring_route_});
 
+// The GlobalMediaControlsCastStartStop flag is disabled on ChromeOS.
+#if BUILDFLAG(IS_CHROMEOS)
+  // A local cast route should show the action icon.
+  UpdateRoutesAndExpectIconShown({local_cast_route_});
+#else
   // A cast route should hide the action icon.
   UpdateRoutesAndExpectIconHidden({local_cast_route_});
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   // A non local route should hide the action icon.
   UpdateRoutesAndExpectIconHidden({non_local_mirroring_route_});
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 062b9b8f..cf811ad 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -44,6 +44,7 @@
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
+#include "chrome/browser/ui/webui/app_settings/web_app_settings_ui.h"
 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
 #include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h"
 #include "chrome/browser/web_applications/manifest_update_manager.h"
@@ -811,9 +812,11 @@
 #else
   mojo::PendingReceiver<app_management::mojom::Page> page;
   mojo::Remote<app_management::mojom::PageHandler> handler;
+  auto delegate =
+      WebAppSettingsUI::CreateAppManagementPageHandlerDelegate(profile());
   AppManagementPageHandler app_management_page_handler(
       handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
-      profile());
+      profile(), *delegate);
   app_management_page_handler.SetWindowMode(app_id,
                                             apps::mojom::WindowMode::kBrowser);
 #endif
@@ -836,9 +839,11 @@
 #else
   mojo::PendingReceiver<app_management::mojom::Page> page;
   mojo::Remote<app_management::mojom::PageHandler> handler;
+  auto delegate =
+      WebAppSettingsUI::CreateAppManagementPageHandlerDelegate(profile());
   AppManagementPageHandler app_management_page_handler(
       handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
-      profile());
+      profile(), *delegate);
   app_management_page_handler.SetWindowMode(app_id,
                                             apps::mojom::WindowMode::kWindow);
 #endif
@@ -1705,9 +1710,11 @@
       << "No app installed for site: " << site_mode;
   mojo::PendingReceiver<app_management::mojom::Page> page;
   mojo::Remote<app_management::mojom::PageHandler> handler;
+  auto delegate =
+      WebAppSettingsUI::CreateAppManagementPageHandlerDelegate(profile());
   AppManagementPageHandler app_management_page_handler(
       handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
-      profile());
+      profile(), *delegate);
   app_management_page_handler.SetRunOnOsLoginMode(app_state->id, login_mode);
 #endif
 }
diff --git a/chrome/browser/ui/webui/app_launcher_page_ui.cc b/chrome/browser/ui/webui/app_launcher_page_ui.cc
index 5280b80..4980e89 100644
--- a/chrome/browser/ui/webui/app_launcher_page_ui.cc
+++ b/chrome/browser/ui/webui/app_launcher_page_ui.cc
@@ -148,7 +148,7 @@
       base::BindRepeating(&AppLauncherPageUI::OnHideWebStoreIconChanged,
                           base::Unretained(this)));
 
-  source->AddBoolean("canShowAppInfoDialog", CanPlatformShowAppInfoDialog());
+  source->AddBoolean("canShowAppInfoDialog", true);
 
   AppLauncherHandler::RegisterLoadTimeData(GetProfile(), source);
 
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index f2fe17e..22894a9 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -127,15 +127,15 @@
 AppManagementPageHandler::AppManagementPageHandler(
     mojo::PendingReceiver<app_management::mojom::PageHandler> receiver,
     mojo::PendingRemote<app_management::mojom::Page> page,
-    Profile* profile)
+    Profile* profile,
+    Delegate& delegate)
     : receiver_(this, std::move(receiver)),
       page_(std::move(page)),
-      profile_(profile)
+      profile_(profile),
+      delegate_(delegate),
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-      ,
-      shelf_delegate_(this, profile)
+      shelf_delegate_(this, profile),
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-      ,
       preferred_apps_list_handle_(
           apps::AppServiceProxyFactory::GetForProfile(profile)
               ->PreferredApps()) {
@@ -243,7 +243,7 @@
 void AppManagementPageHandler::Uninstall(const std::string& app_id) {
   apps::AppServiceProxyFactory::GetForProfile(profile_)->Uninstall(
       app_id, apps::mojom::UninstallSource::kAppManagement,
-      nullptr /* parent_window */);
+      delegate_.GetUninstallAnchorWindow());
 }
 
 void AppManagementPageHandler::OpenNativeSettings(const std::string& app_id) {
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
index 15d4ede..d4258d08 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.h
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
@@ -15,6 +15,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/webui/resources/cr_components/app_management/app_management.mojom-forward.h"
 
 class Profile;
@@ -24,10 +25,23 @@
       public apps::AppRegistryCache::Observer,
       public apps::PreferredAppsListHandle::Observer {
  public:
+  //  Handles platform specific tasks.
+  class Delegate {
+   public:
+    Delegate() = default;
+    Delegate(const Delegate&) = delete;
+    Delegate& operator=(const Delegate&) = delete;
+
+    virtual ~Delegate() = default;
+
+    virtual gfx::NativeWindow GetUninstallAnchorWindow() const = 0;
+  };
+
   AppManagementPageHandler(
       mojo::PendingReceiver<app_management::mojom::PageHandler> receiver,
       mojo::PendingRemote<app_management::mojom::Page> page,
-      Profile* profile);
+      Profile* profile,
+      Delegate& delegate);
 
   AppManagementPageHandler(const AppManagementPageHandler&) = delete;
   AppManagementPageHandler& operator=(const AppManagementPageHandler&) = delete;
@@ -80,6 +94,8 @@
 
   raw_ptr<Profile> profile_;
 
+  Delegate& delegate_;
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   AppManagementShelfDelegate shelf_delegate_;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.cc
index 78e0fb4..35c2888b 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.cc
@@ -11,7 +11,6 @@
 #include "base/feature_list.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_source.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -26,8 +25,9 @@
 #include "ui/webui/resources/cr_components/app_management/app_management.mojom.h"
 
 AppManagementPageHandlerFactory::AppManagementPageHandlerFactory(
-    Profile* profile)
-    : profile_(profile) {}
+    Profile* profile,
+    std::unique_ptr<AppManagementPageHandler::Delegate> delegate)
+    : profile_(profile), delegate_(std::move(delegate)) {}
 
 AppManagementPageHandlerFactory::~AppManagementPageHandlerFactory() = default;
 
@@ -44,5 +44,5 @@
   DCHECK(page);
 
   page_handler_ = std::make_unique<AppManagementPageHandler>(
-      std::move(receiver), std::move(page), profile_);
+      std::move(receiver), std::move(page), profile_, *delegate_);
 }
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h b/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h
index 6e87f75..7449f4e 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -14,12 +15,12 @@
 
 class Profile;
 
-class AppManagementPageHandler;
-
 class AppManagementPageHandlerFactory
     : public app_management::mojom::PageHandlerFactory {
  public:
-  explicit AppManagementPageHandlerFactory(Profile* profile);
+  AppManagementPageHandlerFactory(
+      Profile* profile,
+      std::unique_ptr<AppManagementPageHandler::Delegate> delegate);
 
   AppManagementPageHandlerFactory(const AppManagementPageHandlerFactory&) =
       delete;
@@ -38,12 +39,13 @@
       mojo::PendingReceiver<app_management::mojom::PageHandler> receiver)
       override;
 
+  raw_ptr<Profile> profile_;
+
+  std::unique_ptr<AppManagementPageHandler::Delegate> delegate_;
   std::unique_ptr<AppManagementPageHandler> page_handler_;
 
   mojo::Receiver<app_management::mojom::PageHandlerFactory>
       page_factory_receiver_{this};
-
-  Profile* profile_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_APP_MANAGEMENT_APP_MANAGEMENT_PAGE_HANDLER_FACTORY_H_
diff --git a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
index 4a06f8f..1d7c187 100644
--- a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
+++ b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
@@ -5,7 +5,12 @@
 #include "chrome/browser/ui/webui/app_settings/web_app_settings_ui.h"
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/app_settings_resources.h"
 #include "chrome/grit/app_settings_resources_map.h"
@@ -15,10 +20,15 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
 
 void AddAppManagementStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"title", IDS_WEB_APP_SETTINGS_TITLE},
+      {"appManagementAppInstalledByPolicyLabel",
+       IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
       {"appManagementNotificationsLabel", IDS_APP_MANAGEMENT_NOTIFICATIONS},
       {"appManagementPermissionsLabel", IDS_APP_MANAGEMENT_PERMISSIONS},
       {"appManagementLocationPermissionLabel", IDS_APP_MANAGEMENT_LOCATION},
@@ -33,6 +43,30 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 }
 
+class WebAppSettingsWindowDelegate : public AppManagementPageHandler::Delegate {
+ public:
+  explicit WebAppSettingsWindowDelegate(Profile* profile) : profile_(profile) {}
+
+  ~WebAppSettingsWindowDelegate() override = default;
+
+  gfx::NativeWindow GetUninstallAnchorWindow() const override {
+    return chrome::FindTabbedBrowser(profile_, false)
+        ->window()
+        ->GetNativeWindow();
+  }
+
+ private:
+  raw_ptr<Profile> profile_;
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<AppManagementPageHandler::Delegate>
+WebAppSettingsUI::CreateAppManagementPageHandlerDelegate(Profile* profile) {
+  return std::make_unique<WebAppSettingsWindowDelegate>(profile);
+}
+
 WebAppSettingsUI::WebAppSettingsUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true) {
   // Set up the chrome://app-settings source.
@@ -47,21 +81,35 @@
       base::make_span(kAppSettingsResources, kAppSettingsResourcesSize),
       IDR_APP_SETTINGS_WEB_APP_SETTINGS_HTML);
 
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context, html_source);
+  Profile* profile = Profile::FromWebUI(web_ui);
+  content::WebUIDataSource::Add(profile, html_source);
+
+  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
+  registrar_observation_.Observe(&provider->registrar());
 }
 
 void WebAppSettingsUI::BindInterface(
     mojo::PendingReceiver<app_management::mojom::PageHandlerFactory> receiver) {
   if (!app_management_page_handler_factory_) {
+    auto window_delegate = std::make_unique<WebAppSettingsWindowDelegate>(
+        Profile::FromWebUI(web_ui()));
+
     app_management_page_handler_factory_ =
         std::make_unique<AppManagementPageHandlerFactory>(
-            Profile::FromWebUI(web_ui()));
+            Profile::FromWebUI(web_ui()), std::move(window_delegate));
   }
   app_management_page_handler_factory_->Bind(std::move(receiver));
 }
 
+void WebAppSettingsUI::OnWebAppUninstalled(const web_app::AppId& app_id) {
+  auto* web_contents = web_ui()->GetWebContents();
+  const web_app::AppId current_app_id =
+      web_app::GetAppIdFromAppSettingsUrl(web_contents->GetURL());
+
+  if (app_id == current_app_id)
+    web_contents->ClosePage();
+}
+
 WebAppSettingsUI::~WebAppSettingsUI() = default;
 
 WEB_UI_CONTROLLER_TYPE_IMPL(WebAppSettingsUI)
diff --git a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.h b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.h
index 867948f..c7c9063 100644
--- a/chrome/browser/ui/webui/app_settings/web_app_settings_ui.h
+++ b/chrome/browser/ui/webui/app_settings/web_app_settings_ui.h
@@ -5,12 +5,18 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_APP_SETTINGS_WEB_APP_SETTINGS_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_APP_SETTINGS_WEB_APP_SETTINGS_UI_H_
 
+#include "base/scoped_observation.h"
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h"
+#include "chrome/browser/web_applications/app_registrar_observer.h"
+#include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "ui/webui/mojo_web_ui_controller.h"
 #include "ui/webui/resources/cr_components/app_management/app_management.mojom-forward.h"
 
 // The WebUI for chrome://app-settings
-class WebAppSettingsUI : public ui::MojoWebUIController {
+class WebAppSettingsUI : public ui::MojoWebUIController,
+                         public web_app::AppRegistrarObserver {
  public:
   explicit WebAppSettingsUI(content::WebUI* web_ui);
 
@@ -19,15 +25,25 @@
 
   ~WebAppSettingsUI() override;
 
+  static std::unique_ptr<AppManagementPageHandler::Delegate>
+  CreateAppManagementPageHandlerDelegate(Profile* profile);
+
   // Instantiates implementor of the mojom::PageHandlerFactory mojo interface
   // passing the pending receiver that will be internally bound.
   void BindInterface(
       mojo::PendingReceiver<app_management::mojom::PageHandlerFactory>
           receiver);
 
+  // AppRegistrarObserver:
+  void OnWebAppUninstalled(const web_app::AppId& app_id) override;
+
  private:
   std::unique_ptr<AppManagementPageHandlerFactory>
       app_management_page_handler_factory_;
+  base::ScopedObservation<web_app::WebAppRegistrar,
+                          web_app::AppRegistrarObserver>
+      registrar_observation_{this};
+
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index f39bedf..6c0de272 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -777,10 +777,12 @@
     return &NewWebUI<IdentityInternalsUI>;
   if (url.host_piece() == chrome::kChromeUINewTabHost)
     return &NewWebUI<NewTabUI>;
-  if (url.host_piece() == chrome::kChromeUINewTabPageHost)
-    return &NewWebUI<NewTabPageUI>;
-  if (url.host_piece() == chrome::kChromeUINewTabPageThirdPartyHost)
-    return &NewWebUI<NewTabPageThirdPartyUI>;
+  if (!profile->IsOffTheRecord()) {
+    if (url.host_piece() == chrome::kChromeUINewTabPageHost)
+      return &NewWebUI<NewTabPageUI>;
+    if (url.host_piece() == chrome::kChromeUINewTabPageThirdPartyHost)
+      return &NewWebUI<NewTabPageThirdPartyUI>;
+  }
   if (base::FeatureList::IsEnabled(features::kWebUIFeedback)) {
     if (url.host_piece() == chrome::kChromeUIFeedbackHost)
       return &NewWebUI<FeedbackUI>;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory_browsertest.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory_browsertest.cc
index d368635..4167ead9 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory_browsertest.cc
@@ -40,3 +40,31 @@
   EXPECT_EQ(u"About Version", web_contents->GetTitle());
   EXPECT_TRUE(web_contents->GetWebUI());
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeWebUIControllerFactoryBrowserTest,
+                       NoWebUiNtpInIncognitoProfile) {
+  auto* incognito_browser = CreateIncognitoBrowser();
+  auto* web_contents =
+      incognito_browser->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+      incognito_browser, GURL(chrome::kChromeUINewTabPageURL)));
+  EXPECT_FALSE(web_contents->GetWebUI());
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+      incognito_browser, GURL(chrome::kChromeUINewTabPageThirdPartyURL)));
+  EXPECT_FALSE(web_contents->GetWebUI());
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeWebUIControllerFactoryBrowserTest,
+                       WebUiNtpInNormalProfile) {
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GURL(chrome::kChromeUINewTabPageURL)));
+  EXPECT_TRUE(web_contents->GetWebUI());
+
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GURL(chrome::kChromeUINewTabPageThirdPartyURL)));
+  EXPECT_TRUE(web_contents->GetWebUI());
+}
diff --git a/chrome/browser/ui/webui/chromeos/network_ui.cc b/chrome/browser/ui/webui/chromeos/network_ui.cc
index f5444b7..58f4276 100644
--- a/chrome/browser/ui/webui/chromeos/network_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/network_ui.cc
@@ -109,7 +109,7 @@
   ~NetworkDiagnosticsMessageHandler() override = default;
 
   void RegisterMessages() override {
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         "OpenFeedbackDialog",
         base::BindRepeating(
             &NetworkDiagnosticsMessageHandler::OpenFeedbackDialog,
@@ -117,7 +117,7 @@
   }
 
  private:
-  void OpenFeedbackDialog(const base::ListValue* value) {
+  void OpenFeedbackDialog(base::Value::ConstListView value) {
     chrome::ShowFeedbackPage(nullptr, chrome::kFeedbackSourceNetworkHealthPage,
                              "" /*description_template*/,
                              "" /*description_template_placeholder*/,
@@ -141,50 +141,50 @@
 
   // WebUIMessageHandler implementation.
   void RegisterMessages() override {
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kAddNetwork,
         base::BindRepeating(&NetworkConfigMessageHandler::AddNetwork,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kGetNetworkProperties,
         base::BindRepeating(
             &NetworkConfigMessageHandler::GetShillNetworkProperties,
             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kGetDeviceProperties,
         base::BindRepeating(
             &NetworkConfigMessageHandler::GetShillDeviceProperties,
             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kGetEthernetEAP,
         base::BindRepeating(&NetworkConfigMessageHandler::GetShillEthernetEAP,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kOpenCellularActivationUi,
         base::BindRepeating(
             &NetworkConfigMessageHandler::OpenCellularActivationUi,
             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kResetESimCache,
         base::BindRepeating(&NetworkConfigMessageHandler::ResetESimCache,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kShowNetworkDetails,
         base::BindRepeating(&NetworkConfigMessageHandler::ShowNetworkDetails,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kShowNetworkConfig,
         base::BindRepeating(&NetworkConfigMessageHandler::ShowNetworkConfig,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kShowAddNewWifiNetworkDialog,
         base::BindRepeating(&NetworkConfigMessageHandler::ShowAddNewWifi,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kGetHostname,
         base::BindRepeating(&NetworkConfigMessageHandler::GetHostname,
                             base::Unretained(this)));
-    web_ui()->RegisterDeprecatedMessageCallback(
+    web_ui()->RegisterMessageCallback(
         kSetHostname,
         base::BindRepeating(&NetworkConfigMessageHandler::SetHostname,
                             base::Unretained(this)));
@@ -196,10 +196,10 @@
     ResolveJavascriptCallback(base::Value(callback_id), response);
   }
 
-  void GetShillNetworkProperties(const base::ListValue* arg_list) {
-    CHECK_EQ(2u, arg_list->GetListDeprecated().size());
-    std::string callback_id = arg_list->GetListDeprecated()[0].GetString();
-    std::string guid = arg_list->GetListDeprecated()[1].GetString();
+  void GetShillNetworkProperties(base::Value::ConstListView arg_list) {
+    CHECK_EQ(2u, arg_list.size());
+    std::string callback_id = arg_list[0].GetString();
+    std::string guid = arg_list[1].GetString();
 
     std::string service_path;
     if (!GetServicePathFromGuid(guid, &service_path)) {
@@ -231,10 +231,10 @@
     Respond(callback_id, return_arg_list);
   }
 
-  void GetShillDeviceProperties(const base::ListValue* arg_list) {
-    CHECK_EQ(2u, arg_list->GetListDeprecated().size());
-    std::string callback_id = arg_list->GetListDeprecated()[0].GetString();
-    std::string type = arg_list->GetListDeprecated()[1].GetString();
+  void GetShillDeviceProperties(base::Value::ConstListView arg_list) {
+    CHECK_EQ(2u, arg_list.size());
+    std::string callback_id = arg_list[0].GetString();
+    std::string type = arg_list[1].GetString();
 
     const DeviceState* device =
         NetworkHandler::Get()->network_state_handler()->GetDeviceStateByType(
@@ -250,9 +250,9 @@
                        weak_ptr_factory_.GetWeakPtr(), callback_id, type));
   }
 
-  void GetShillEthernetEAP(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string callback_id = arg_list->GetListDeprecated()[0].GetString();
+  void GetShillEthernetEAP(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string callback_id = arg_list[0].GetString();
 
     NetworkStateHandler::NetworkStateList list;
     NetworkHandler::Get()->network_state_handler()->GetNetworkListByType(
@@ -274,9 +274,9 @@
     Respond(callback_id, response);
   }
 
-  void OpenCellularActivationUi(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string callback_id = arg_list->GetListDeprecated()[0].GetString();
+  void OpenCellularActivationUi(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string callback_id = arg_list[0].GetString();
 
     const NetworkState* cellular_network =
         NetworkHandler::Get()->network_state_handler()->FirstNetworkByType(
@@ -290,7 +290,7 @@
     Respond(callback_id, response);
   }
 
-  void ResetESimCache(const base::ListValue* arg_list) {
+  void ResetESimCache(base::Value::ConstListView arg_list) {
     CellularESimProfileHandler* handler =
         NetworkHandler::Get()->cellular_esim_profile_handler();
     if (!handler)
@@ -301,21 +301,21 @@
     handler_impl->ResetESimProfileCache();
   }
 
-  void ShowNetworkDetails(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string guid = arg_list->GetListDeprecated()[0].GetString();
+  void ShowNetworkDetails(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string guid = arg_list[0].GetString();
 
     InternetDetailDialog::ShowDialog(guid);
   }
 
-  void ShowNetworkConfig(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string guid = arg_list->GetListDeprecated()[0].GetString();
+  void ShowNetworkConfig(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string guid = arg_list[0].GetString();
 
     InternetConfigDialog::ShowDialogForNetworkId(guid);
   }
 
-  void ShowAddNewWifi(const base::ListValue* arg_list) {
+  void ShowAddNewWifi(base::Value::ConstListView arg_list) {
     InternetConfigDialog::ShowDialogForNetworkType(::onc::network_type::kWiFi);
   }
 
@@ -337,17 +337,17 @@
     Respond(callback_id, return_arg_list);
   }
 
-  void GetHostname(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string callback_id = arg_list->GetListDeprecated()[0].GetString();
+  void GetHostname(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string callback_id = arg_list[0].GetString();
     std::string hostname =
         NetworkHandler::Get()->network_state_handler()->hostname();
     Respond(callback_id, base::Value(hostname));
   }
 
-  void SetHostname(const base::ListValue* arg_list) {
-    CHECK_EQ(1u, arg_list->GetListDeprecated().size());
-    std::string hostname = arg_list->GetListDeprecated()[0].GetString();
+  void SetHostname(base::Value::ConstListView arg_list) {
+    CHECK_EQ(1u, arg_list.size());
+    std::string hostname = arg_list[0].GetString();
     NET_LOG(USER) << "SET HOSTNAME: " << hostname;
     NetworkHandler::Get()->network_state_handler()->SetHostname(hostname);
   }
@@ -376,9 +376,9 @@
     Respond(callback_id, return_arg_list);
   }
 
-  void AddNetwork(const base::ListValue* args) {
-    DCHECK(!args->GetListDeprecated().empty());
-    std::string onc_type = args->GetListDeprecated()[0].GetString();
+  void AddNetwork(base::Value::ConstListView args) {
+    DCHECK(!args.empty());
+    std::string onc_type = args[0].GetString();
     InternetConfigDialog::ShowDialogForNetworkType(onc_type);
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 5f474ea..684821a 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -459,50 +459,50 @@
 PrintPreviewHandler::~PrintPreviewHandler() = default;
 
 void PrintPreviewHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPrinters",
       base::BindRepeating(&PrintPreviewHandler::HandleGetPrinters,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPreview", base::BindRepeating(&PrintPreviewHandler::HandleGetPreview,
                                         base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "print", base::BindRepeating(&PrintPreviewHandler::HandlePrint,
                                    base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPrinterCapabilities",
       base::BindRepeating(&PrintPreviewHandler::HandleGetPrinterCapabilities,
                           base::Unretained(this)));
 #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "showSystemDialog",
       base::BindRepeating(&PrintPreviewHandler::HandleShowSystemDialog,
                           base::Unretained(this)));
 #endif
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "signIn", base::BindRepeating(&PrintPreviewHandler::HandleSignin,
                                     base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "closePrintPreviewDialog",
       base::BindRepeating(&PrintPreviewHandler::HandleClosePreviewDialog,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "hidePreview",
       base::BindRepeating(&PrintPreviewHandler::HandleHidePreview,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "cancelPendingPrintRequest",
       base::BindRepeating(&PrintPreviewHandler::HandleCancelPendingPrintRequest,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "saveAppState",
       base::BindRepeating(&PrintPreviewHandler::HandleSaveAppState,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getInitialSettings",
       base::BindRepeating(&PrintPreviewHandler::HandleGetInitialSettings,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "managePrinters",
       base::BindRepeating(&PrintPreviewHandler::HandleManagePrinters,
                           base::Unretained(this)));
@@ -622,12 +622,11 @@
   return result;
 }
 
-void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* args) {
-  const auto& list = args->GetListDeprecated();
-  CHECK_GE(list.size(), 2u);
-  std::string callback_id = list[0].GetString();
+void PrintPreviewHandler::HandleGetPrinters(base::Value::ConstListView args) {
+  CHECK_GE(args.size(), 2u);
+  std::string callback_id = args[0].GetString();
   CHECK(!callback_id.empty());
-  int type = list[1].GetInt();
+  int type = args[1].GetInt();
   mojom::PrinterType printer_type = static_cast<mojom::PrinterType>(type);
 
   // Immediately resolve the callback without fetching printers if the printer
@@ -653,21 +652,20 @@
 }
 
 void PrintPreviewHandler::HandleGetPrinterCapabilities(
-    const base::ListValue* args) {
-  const auto& list = args->GetListDeprecated();
+    base::Value::ConstListView args) {
   // Validate that we have a valid callback_id
-  if (list.size() < 1 || !list[0].is_string() || list[0].GetString().empty()) {
+  if (args.size() < 1 || !args[0].is_string() || args[0].GetString().empty()) {
     RejectJavascriptCallback(base::Value(""), base::Value());
     return;
   }
   // If we got here, we know that we have at least one string element.
-  std::string callback_id = list[0].GetString();
-  if (list.size() < 3) {
+  std::string callback_id = args[0].GetString();
+  if (args.size() < 3) {
     RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
-  const std::string* printer_name = list[1].GetIfString();
-  absl::optional<int> type = list[2].GetIfInt();
+  const std::string* printer_name = args[1].GetIfString();
+  absl::optional<int> type = args[2].GetIfInt();
   if (!printer_name || printer_name->empty() || !type.has_value()) {
     RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
@@ -692,16 +690,16 @@
                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
-  DCHECK_EQ(2U, args->GetListDeprecated().size());
+void PrintPreviewHandler::HandleGetPreview(base::Value::ConstListView args) {
+  DCHECK_EQ(2U, args.size());
   std::string callback_id;
   std::string json_str;
 
   // All of the conditions below should be guaranteed by the print preview
   // javascript.
-  callback_id = args->GetListDeprecated()[0].GetString();
+  callback_id = args[0].GetString();
   CHECK(!callback_id.empty());
-  json_str = args->GetListDeprecated()[1].GetString();
+  json_str = args[1].GetString();
   base::Value settings = GetSettingsDictionary(json_str);
   CHECK(settings.is_dict());
   int request_id = settings.FindIntKey(kPreviewRequestID).value();
@@ -764,14 +762,14 @@
   last_preview_settings_ = std::move(settings);
 }
 
-void PrintPreviewHandler::HandlePrint(const base::ListValue* args) {
+void PrintPreviewHandler::HandlePrint(base::Value::ConstListView args) {
   ReportRegeneratePreviewRequestCountBeforePrint(
       regenerate_preview_request_count_);
-  CHECK(args->GetListDeprecated()[0].is_string());
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+  CHECK(args[0].is_string());
+  std::string callback_id = args[0].GetString();
   CHECK(!callback_id.empty());
-  CHECK(args->GetListDeprecated()[1].is_string());
-  std::string json_str = args->GetListDeprecated()[1].GetString();
+  CHECK(args[1].is_string());
+  std::string json_str = args[1].GetString();
 
   base::Value settings = GetSettingsDictionary(json_str);
   if (!settings.is_dict()) {
@@ -827,30 +825,31 @@
                                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void PrintPreviewHandler::HandleHidePreview(const base::ListValue* /*args*/) {
+void PrintPreviewHandler::HandleHidePreview(
+    base::Value::ConstListView /*args*/) {
   print_preview_ui()->OnHidePreviewDialog();
 }
 
 void PrintPreviewHandler::HandleCancelPendingPrintRequest(
-    const base::ListValue* /*args*/) {
+    base::Value::ConstListView /*args*/) {
   WebContents* initiator = GetInitiator();
   if (initiator)
     ClearInitiatorDetails();
   ShowPrintErrorDialog();
 }
 
-void PrintPreviewHandler::HandleSaveAppState(const base::ListValue* args) {
+void PrintPreviewHandler::HandleSaveAppState(base::Value::ConstListView args) {
   std::string data_to_save;
   PrintPreviewStickySettings* sticky_settings =
       PrintPreviewStickySettings::GetInstance();
-  if (args->GetListDeprecated()[0].is_string())
-    data_to_save = args->GetListDeprecated()[0].GetString();
+  if (args[0].is_string())
+    data_to_save = args[0].GetString();
   if (!data_to_save.empty())
     sticky_settings->StoreAppState(data_to_save);
   sticky_settings->SaveInPrefs(GetPrefs());
 }
 
-void PrintPreviewHandler::HandleSignin(const base::ListValue* /*args*/) {
+void PrintPreviewHandler::HandleSignin(base::Value::ConstListView /*args*/) {
   Profile* profile = Profile::FromWebUI(web_ui());
   DCHECK(profile);
 
@@ -879,7 +878,7 @@
 
 #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
 void PrintPreviewHandler::HandleShowSystemDialog(
-    const base::ListValue* /*args*/) {
+    base::Value::ConstListView /*args*/) {
   ReportUserActionHistogram(
       UserActionBuckets::kFallbackToAdvancedSettingsDialog);
 
@@ -900,7 +899,7 @@
 #endif
 
 void PrintPreviewHandler::HandleClosePreviewDialog(
-    const base::ListValue* /*args*/) {
+    base::Value::ConstListView /*args*/) {
   ReportUserActionHistogram(UserActionBuckets::kCancel);
 
   ReportRegeneratePreviewRequestCountBeforeCancel(
@@ -935,9 +934,9 @@
 }
 
 void PrintPreviewHandler::HandleGetInitialSettings(
-    const base::ListValue* args) {
-  CHECK(args->GetListDeprecated()[0].is_string());
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+    base::Value::ConstListView args) {
+  CHECK(args[0].is_string());
+  std::string callback_id = args[0].GetString();
   CHECK(!callback_id.empty());
 
   AllowJavascript();
@@ -1196,7 +1195,8 @@
 }
 
 void PrintPreviewHandler::OnPrintRequestCancelled() {
-  HandleCancelPendingPrintRequest(nullptr);
+  base::Value empty(base::Value::Type::LIST);
+  HandleCancelPendingPrintRequest(empty.GetList());
 }
 
 void PrintPreviewHandler::ClearInitiatorDetails() {
@@ -1305,7 +1305,8 @@
   GetPdfPrinterHandler()->SetPdfSavedClosureForTesting(std::move(closure));
 }
 
-void PrintPreviewHandler::HandleManagePrinters(const base::ListValue* args) {
+void PrintPreviewHandler::HandleManagePrinters(
+    base::Value::ConstListView args) {
 #if BUILDFLAG(IS_CHROMEOS)
   if (!local_printer_) {
     LOG(ERROR) << "Local printer not available";
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index a79d692..e57d7db 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -168,57 +168,57 @@
 
   // Gets the list of printers. First element of |args| is the Javascript
   // callback, second element of |args| is the printer type to fetch.
-  void HandleGetPrinters(const base::ListValue* args);
+  void HandleGetPrinters(base::Value::ConstListView args);
 
   // Asks the initiator renderer to generate a preview.  First element of |args|
   // is a job settings JSON string.
-  void HandleGetPreview(const base::ListValue* args);
+  void HandleGetPreview(base::Value::ConstListView args);
 
   // Gets the job settings from Web UI and initiate printing. First element of
   // |args| is a job settings JSON string.
-  void HandlePrint(const base::ListValue* args);
+  void HandlePrint(base::Value::ConstListView args);
 
   // Handles the request to hide the preview dialog for printing.
   // |args| is unused.
-  void HandleHidePreview(const base::ListValue* args);
+  void HandleHidePreview(base::Value::ConstListView args);
 
   // Handles the request to cancel the pending print request. |args| is unused.
-  void HandleCancelPendingPrintRequest(const base::ListValue* args);
+  void HandleCancelPendingPrintRequest(base::Value::ConstListView args);
 
   // Handles a request to store data that the web ui wishes to persist.
   // First element of |args| is the data to persist.
-  void HandleSaveAppState(const base::ListValue* args);
+  void HandleSaveAppState(base::Value::ConstListView args);
 
   // Gets the printer capabilities. Fist element of |args| is the Javascript
   // callback, second element is the printer ID of the printer whose
   // capabilities are requested, and the third element is the type of the
   // printer whose capabilities are requested.
-  void HandleGetPrinterCapabilities(const base::ListValue* args);
+  void HandleGetPrinterCapabilities(base::Value::ConstListView args);
 
 #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
   // Asks the initiator renderer to show the native print system dialog. |args|
   // is unused.
-  void HandleShowSystemDialog(const base::ListValue* args);
+  void HandleShowSystemDialog(base::Value::ConstListView args);
 #endif
 
   // Opens a new tab to allow the user to add an account to sign into cloud
   // print. |args| is unused.
-  void HandleSignin(const base::ListValue* args);
+  void HandleSignin(base::Value::ConstListView args);
 
   // Called when the tab opened by HandleSignIn() is closed.
   void OnSignInTabClosed();
 
   // Gathers UMA stats when the print preview dialog is about to close.
   // |args| is unused.
-  void HandleClosePreviewDialog(const base::ListValue* args);
+  void HandleClosePreviewDialog(base::Value::ConstListView args);
 
   // Asks the browser for several settings that are needed before the first
   // preview is displayed.
-  void HandleGetInitialSettings(const base::ListValue* args);
+  void HandleGetInitialSettings(base::Value::ConstListView args);
 
   // Opens printer settings in the Chrome OS Settings App or OS's printer manger
   // dialog. |args| is unused.
-  void HandleManagePrinters(const base::ListValue* args);
+  void HandleManagePrinters(base::Value::ConstListView args);
 
   void SendInitialSettings(const std::string& callback_id,
                            base::Value policies,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index 15d9c4fa..1536158 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -137,15 +137,14 @@
   return print_ticket;
 }
 
-std::unique_ptr<base::ListValue> ConstructPreviewArgs(
-    base::StringPiece callback_id,
-    const base::Value& print_ticket) {
+base::Value ConstructPreviewArgs(base::StringPiece callback_id,
+                                 const base::Value& print_ticket) {
   base::Value args(base::Value::Type::LIST);
   args.Append(callback_id);
   std::string json;
   base::JSONWriter::Write(print_ticket, &json);
   args.Append(json);
-  return base::ListValue::From(base::Value::ToUniquePtrValue(std::move(args)));
+  return args;
 }
 
 UserActionBuckets GetUserActionForPrinterType(mojom::PrinterType type) {
@@ -460,8 +459,6 @@
     // before any other messages are sent.
     base::Value args(base::Value::Type::LIST);
     args.Append("test-callback-id-0");
-    std::unique_ptr<base::ListValue> list_args =
-        base::ListValue::From(base::Value::ToUniquePtrValue(std::move(args)));
 
     auto* browser_process = TestingBrowserProcess::GetGlobal();
     std::string original_locale = browser_process->GetApplicationLocale();
@@ -472,7 +469,7 @@
       browser_process->SetApplicationLocale(locale);
       base::test::ScopedRestoreICUDefaultLocale scoped_locale(locale);
       base::ResetFormattersForTesting();
-      handler()->HandleGetInitialSettings(list_args.get());
+      handler()->HandleGetInitialSettings(args.GetList());
     }
     // Reset again now that |scoped_locale| has been destroyed.
     browser_process->SetApplicationLocale(original_locale);
@@ -632,7 +629,7 @@
     base::Value args(base::Value::Type::LIST);
     args.Append(callback_id_in);
     args.Append(static_cast<int>(type));
-    handler()->HandleGetPrinters(&base::Value::AsListValue(args));
+    handler()->HandleGetPrinters(args.GetList());
   }
 
   // Validates that the printers-added Web UI event has been fired for
@@ -664,7 +661,7 @@
     args.Append(callback_id_in);
     args.Append(printer_name);
     args.Append(static_cast<int>(type));
-    handler()->HandleGetPrinterCapabilities(&base::Value::AsListValue(args));
+    handler()->HandleGetPrinterCapabilities(args.GetList());
   }
 
   // Validates that a printer capabilities promise was resolved/rejected.
@@ -1240,9 +1237,9 @@
     preview_ticket.SetIntKey(kPreviewRequestID, i);
     std::string preview_callback_id =
         "test-callback-id-" + base::NumberToString(2 * i + 1);
-    std::unique_ptr<base::ListValue> preview_list_args =
+    base::Value preview_list_args =
         ConstructPreviewArgs(preview_callback_id, preview_ticket);
-    handler()->HandleGetPreview(preview_list_args.get());
+    handler()->HandleGetPreview(preview_list_args.GetList());
 
     // Send printing request.
     mojom::PrinterType type = kAllTypes[i];
@@ -1254,9 +1251,7 @@
     std::string json;
     base::JSONWriter::Write(print_ticket, &json);
     print_args.Append(json);
-    std::unique_ptr<base::ListValue> print_list_args = base::ListValue::From(
-        base::Value::ToUniquePtrValue(std::move(print_args)));
-    handler()->HandlePrint(print_list_args.get());
+    handler()->HandlePrint(print_args.GetList());
 
     CheckHistograms(histograms, type);
 
@@ -1291,9 +1286,9 @@
   print_render_frame.SetCompletionClosure(run_loop.QuitClosure());
 
   base::Value print_ticket = GetPrintPreviewTicket();
-  std::unique_ptr<base::ListValue> list_args =
+  base::Value list_args =
       ConstructPreviewArgs("test-callback-id-1", print_ticket);
-  handler()->HandleGetPreview(list_args.get());
+  handler()->HandleGetPreview(list_args.GetList());
   run_loop.Run();
 
   // Verify that the preview was requested from the renderer with the
@@ -1322,9 +1317,8 @@
 
   const char callback_id_in[] = "test-callback-id-1";
   base::Value print_ticket = GetPrintPreviewTicket();
-  std::unique_ptr<base::ListValue> list_args =
-      ConstructPreviewArgs(callback_id_in, print_ticket);
-  handler()->HandleGetPreview(list_args.get());
+  base::Value list_args = ConstructPreviewArgs(callback_id_in, print_ticket);
+  handler()->HandleGetPreview(list_args.GetList());
   run_loop.Run();
   const base::Value& preview_params = print_render_frame.GetSettings();
 
@@ -1442,9 +1436,7 @@
     args.Append(callback_id_in);
     args.Append(kDummyPrinterName);
     args.Append(static_cast<int>(type));
-    std::unique_ptr<base::ListValue> list_args =
-        base::ListValue::From(base::Value::ToUniquePtrValue(std::move(args)));
-    handler()->HandleGetPrinterCapabilities(list_args.get());
+    handler()->HandleGetPrinterCapabilities(args.GetList());
     EXPECT_TRUE(handler()->CalledOnlyForType(type));
 
     // Start with 1 call from initial settings, then add 1 more for each loop
diff --git a/chrome/browser/ui/webui/read_later/read_later_ui.cc b/chrome/browser/ui/webui/read_later/read_later_ui.cc
index d34a6590..239c372 100644
--- a/chrome/browser/ui/webui/read_later/read_later_ui.cc
+++ b/chrome/browser/ui/webui/read_later/read_later_ui.cc
@@ -155,9 +155,11 @@
 }
 
 void ReadLaterUI::CreatePageHandler(
+    mojo::PendingRemote<reader_mode::mojom::Page> page,
     mojo::PendingReceiver<reader_mode::mojom::PageHandler> receiver) {
-  reader_mode_page_handler_ =
-      std::make_unique<ReaderModePageHandler>(std::move(receiver));
+  DCHECK(page);
+  reader_mode_page_handler_ = std::make_unique<ReaderModePageHandler>(
+      std::move(page), std::move(receiver));
 }
 
 void ReadLaterUI::SetActiveTabURL(const GURL& url) {
diff --git a/chrome/browser/ui/webui/read_later/read_later_ui.h b/chrome/browser/ui/webui/read_later/read_later_ui.h
index ba85555..797e0b78 100644
--- a/chrome/browser/ui/webui/read_later/read_later_ui.h
+++ b/chrome/browser/ui/webui/read_later/read_later_ui.h
@@ -57,6 +57,7 @@
 
   // reader_mode::mojom::PageHandlerFactory:
   void CreatePageHandler(
+      mojo::PendingRemote<reader_mode::mojom::Page> page,
       mojo::PendingReceiver<reader_mode::mojom::PageHandler> receiver) override;
 
   std::unique_ptr<ReadLaterPageHandler> page_handler_;
diff --git a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode.mojom b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode.mojom
index 6ff37f2..88cdd77 100644
--- a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode.mojom
+++ b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode.mojom
@@ -8,13 +8,20 @@
 // Used by the WebUI page to bootstrap bidirectional communication.
 interface PageHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
-  CreatePageHandler(pending_receiver<PageHandler> handler);
+  CreatePageHandler(pending_remote<Page> page,
+                    pending_receiver<PageHandler> handler);
 };
 
 // Browser-side handler for requests from WebUI page.
 interface PageHandler {
-  // Shows Reader Mode. Result is a list of paragraphs from a web page, where
-  // each paragraph is considered essential (is part of the main content of the
-  // web page).
-  ShowReaderMode() => (array<string> result);
+  // Notify the backend that the UI is ready to be shown.
+  ShowUI();
+};
+
+// WebUI-side handler for requests from the browser.
+interface Page {
+  // Trigger the frontend to update the current reader mode display. Essential
+  // content is a list of paragraphs from a web page, where each paragraph is
+  // considered essential (is part of the main content of the web page).
+  OnEssentialContent(array<string> essential_content);
 };
diff --git a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.cc b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.cc
index b1b1bec..e5a2c52 100644
--- a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.cc
+++ b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.cc
@@ -78,12 +78,13 @@
 }  // namespace
 
 ReaderModePageHandler::ReaderModePageHandler(
+    mojo::PendingRemote<reader_mode::mojom::Page> page,
     mojo::PendingReceiver<reader_mode::mojom::PageHandler> receiver)
-    : receiver_(this, std::move(receiver)) {}
+    : receiver_(this, std::move(receiver)), page_(std::move(page)) {}
 
 ReaderModePageHandler::~ReaderModePageHandler() = default;
 
-void ReaderModePageHandler::ShowReaderMode(ShowReaderModeCallback callback) {
+void ReaderModePageHandler::ShowUI() {
   Browser* browser = chrome::FindLastActive();
   if (!browser)
     return;
@@ -94,14 +95,13 @@
   // This will include subframe content for any subframes loaded at this point.
   web_contents->RequestAXTreeSnapshot(
       base::BindOnce(&ReaderModePageHandler::CombineTextNodesAndMakeCallback,
-                     weak_pointer_factory_.GetWeakPtr(), std::move(callback)),
+                     weak_pointer_factory_.GetWeakPtr()),
       ui::AXMode::kWebContents,
       /* exclude_offscreen= */ false, kMaxNodes,
       /* timeout= */ {});
 }
 
 void ReaderModePageHandler::CombineTextNodesAndMakeCallback(
-    ShowReaderModeCallback callback,
     const ui::AXTreeUpdate& update) {
   ui::AXTree tree;
   bool success = tree.Unserialize(update);
@@ -118,5 +118,5 @@
   text_node_contents.reserve(update.nodes.size());
   AddTextNodesToVector(reader_root, &text_node_contents);
 
-  std::move(callback).Run(text_node_contents);
+  page_->OnEssentialContent(std::move(text_node_contents));
 }
diff --git a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.h b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.h
index 62cc988e..dc7b3ba 100644
--- a/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.h
+++ b/chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode_page_handler.h
@@ -8,7 +8,9 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webui/read_later/side_panel/reader_mode/reader_mode.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace ui {
 struct AXTreeUpdate;
@@ -17,19 +19,20 @@
 class ReaderModePageHandler : public reader_mode::mojom::PageHandler {
  public:
   explicit ReaderModePageHandler(
+      mojo::PendingRemote<reader_mode::mojom::Page> page,
       mojo::PendingReceiver<reader_mode::mojom::PageHandler> receiver);
   ReaderModePageHandler(const ReaderModePageHandler&) = delete;
   ReaderModePageHandler& operator=(const ReaderModePageHandler&) = delete;
   ~ReaderModePageHandler() override;
 
   // reader_mode::mojom::PageHandler:
-  void ShowReaderMode(ShowReaderModeCallback callback) override;
+  void ShowUI() override;
 
  private:
-  void CombineTextNodesAndMakeCallback(ShowReaderModeCallback callback,
-                                       const ui::AXTreeUpdate& update);
+  void CombineTextNodesAndMakeCallback(const ui::AXTreeUpdate& update);
 
   mojo::Receiver<reader_mode::mojom::PageHandler> receiver_;
+  mojo::Remote<reader_mode::mojom::Page> page_;
   base::WeakPtrFactory<ReaderModePageHandler> weak_pointer_factory_{this};
 };
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index 557d1c0..b56dfc0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -35,6 +35,21 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
+
+class AppManagementDelegate : public AppManagementPageHandler::Delegate {
+ public:
+  AppManagementDelegate() = default;
+  ~AppManagementDelegate() override = default;
+
+  gfx::NativeWindow GetUninstallAnchorWindow() const override {
+    return nullptr;
+  }
+};
+
+}  // namespace
 
 namespace chromeos {
 namespace settings {
@@ -127,9 +142,10 @@
 void OSSettingsUI::BindInterface(
     mojo::PendingReceiver<app_management::mojom::PageHandlerFactory> receiver) {
   if (!app_management_page_handler_factory_) {
+    auto delegate = std::make_unique<AppManagementDelegate>();
     app_management_page_handler_factory_ =
         std::make_unique<AppManagementPageHandlerFactory>(
-            Profile::FromWebUI(web_ui()));
+            Profile::FromWebUI(web_ui()), std::move(delegate));
   }
   app_management_page_handler_factory_->Bind(std::move(receiver));
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
index 578020c..b01c61f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/time/time.h"
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
 #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
index 71c7fb16..0c0f3282 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
@@ -14,7 +14,6 @@
 #include "base/callback.h"
 #include "base/containers/contains.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h"
@@ -47,8 +46,8 @@
 }
 
 void HandleClose(base::RepeatingClosure close_callback,
-                 const base::ListValue* args) {
-  DCHECK_EQ(0u, args->GetListDeprecated().size());
+                 base::Value::ConstListView args) {
+  DCHECK_EQ(0u, args.size());
   close_callback.Run();
 }
 
@@ -96,15 +95,15 @@
 SecurityKeysPINHandler::~SecurityKeysPINHandler() = default;
 
 void SecurityKeysPINHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyStartSetPIN",
       base::BindRepeating(&SecurityKeysPINHandler::HandleStartSetPIN,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeySetPIN",
       base::BindRepeating(&SecurityKeysPINHandler::HandleSetPIN,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyPINClose",
       base::BindRepeating(&HandleClose,
                           base::BindRepeating(&SecurityKeysPINHandler::Close,
@@ -119,14 +118,15 @@
   callback_id_.clear();
 }
 
-void SecurityKeysPINHandler::HandleStartSetPIN(const base::ListValue* args) {
+void SecurityKeysPINHandler::HandleStartSetPIN(
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(State::kNone, state_);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
 
   AllowJavascript();
   DCHECK(callback_id_.empty());
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   state_ = State::kStartSetPIN;
   set_pin_ = std::make_unique<device::SetPINRequestHandler>(
       supported_transports(),
@@ -180,15 +180,15 @@
                             base::Value(std::move(response)));
 }
 
-void SecurityKeysPINHandler::HandleSetPIN(const base::ListValue* args) {
+void SecurityKeysPINHandler::HandleSetPIN(base::Value::ConstListView args) {
   DCHECK(state_ == State::kGatherNewPIN || state_ == State::kGatherChangePIN);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(3u, args->GetListDeprecated().size());
+  DCHECK_EQ(3u, args.size());
 
   DCHECK(callback_id_.empty());
-  callback_id_ = args->GetListDeprecated()[0].GetString();
-  const std::string old_pin = args->GetListDeprecated()[1].GetString();
-  const std::string new_pin = args->GetListDeprecated()[2].GetString();
+  callback_id_ = args[0].GetString();
+  const std::string old_pin = args[1].GetString();
+  const std::string new_pin = args[2].GetString();
 
   DCHECK((state_ == State::kGatherNewPIN) == old_pin.empty());
 
@@ -202,15 +202,15 @@
 SecurityKeysResetHandler::~SecurityKeysResetHandler() = default;
 
 void SecurityKeysResetHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyReset",
       base::BindRepeating(&SecurityKeysResetHandler::HandleReset,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCompleteReset",
       base::BindRepeating(&SecurityKeysResetHandler::HandleCompleteReset,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyResetClose",
       base::BindRepeating(&HandleClose,
                           base::BindRepeating(&SecurityKeysResetHandler::Close,
@@ -225,14 +225,14 @@
   callback_id_.clear();
 }
 
-void SecurityKeysResetHandler::HandleReset(const base::ListValue* args) {
+void SecurityKeysResetHandler::HandleReset(base::Value::ConstListView args) {
   DCHECK_EQ(State::kNone, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
 
   AllowJavascript();
   DCHECK(callback_id_.empty());
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
 
   state_ = State::kStartReset;
   reset_ = std::make_unique<device::ResetRequestHandler>(
@@ -256,12 +256,12 @@
 }
 
 void SecurityKeysResetHandler::HandleCompleteReset(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
 
   DCHECK(callback_id_.empty());
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
 
   switch (state_) {
     case State::kWaitingForResetNoCallbackYet:
@@ -319,15 +319,16 @@
     : SecurityKeysHandlerBase(std::move(discovery_factory)) {}
 SecurityKeysCredentialHandler::~SecurityKeysCredentialHandler() = default;
 
-void SecurityKeysCredentialHandler::HandleStart(const base::ListValue* args) {
+void SecurityKeysCredentialHandler::HandleStart(
+    base::Value::ConstListView args) {
   DCHECK_EQ(State::kNone, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK(!credential_management_);
 
   AllowJavascript();
   DCHECK(callback_id_.empty());
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
 
   state_ = State::kStart;
   credential_management_ =
@@ -343,28 +344,28 @@
 }
 
 void SecurityKeysCredentialHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementStart",
       base::BindRepeating(&SecurityKeysCredentialHandler::HandleStart,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementPIN",
       base::BindRepeating(&SecurityKeysCredentialHandler::HandlePIN,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementEnumerate",
       base::BindRepeating(&SecurityKeysCredentialHandler::HandleEnumerate,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementDelete",
       base::BindRepeating(&SecurityKeysCredentialHandler::HandleDelete,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementUpdate",
       base::BindRepeating(
           &SecurityKeysCredentialHandler::HandleUpdateUserInformation,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyCredentialManagementClose",
       base::BindRepeating(
           &HandleClose,
@@ -382,47 +383,47 @@
   DCHECK(!credential_management_provide_pin_cb_);
 }
 
-void SecurityKeysCredentialHandler::HandlePIN(const base::ListValue* args) {
+void SecurityKeysCredentialHandler::HandlePIN(base::Value::ConstListView args) {
   DCHECK_EQ(State::kPIN, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(2u, args->GetListDeprecated().size());
+  DCHECK_EQ(2u, args.size());
   DCHECK(credential_management_);
   DCHECK(credential_management_provide_pin_cb_);
   DCHECK(callback_id_.empty());
 
-  callback_id_ = args->GetListDeprecated()[0].GetString();
-  std::string pin = args->GetListDeprecated()[1].GetString();
+  callback_id_ = args[0].GetString();
+  std::string pin = args[1].GetString();
 
   std::move(credential_management_provide_pin_cb_).Run(pin);
 }
 
 void SecurityKeysCredentialHandler::HandleEnumerate(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_EQ(state_, State::kReady);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK(credential_management_);
   DCHECK(callback_id_.empty());
 
   state_ = State::kGettingCredentials;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   credential_management_->GetCredentials(
       base::BindOnce(&SecurityKeysCredentialHandler::OnHaveCredentials,
                      weak_factory_.GetWeakPtr()));
 }
 
-void SecurityKeysCredentialHandler::HandleDelete(const base::ListValue* args) {
+void SecurityKeysCredentialHandler::HandleDelete(
+    base::Value::ConstListView args) {
   DCHECK_EQ(State::kReady, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(2u, args->GetListDeprecated().size());
+  DCHECK_EQ(2u, args.size());
   DCHECK(credential_management_);
   DCHECK(callback_id_.empty());
 
   state_ = State::kDeletingCredentials;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   std::vector<device::PublicKeyCredentialDescriptor> credential_ids;
-  for (const base::Value& el :
-       args->GetListDeprecated()[1].GetListDeprecated()) {
+  for (const base::Value& el : args[1].GetList()) {
     std::vector<uint8_t> credential_id_bytes;
     if (!base::HexStringToBytes(el.GetString(), &credential_id_bytes)) {
       NOTREACHED();
@@ -439,31 +440,29 @@
 }
 
 void SecurityKeysCredentialHandler::HandleUpdateUserInformation(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_EQ(State::kReady, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(5u, args->GetListDeprecated().size());
+  DCHECK_EQ(5u, args.size());
   DCHECK(credential_management_);
   DCHECK(callback_id_.empty());
 
   state_ = State::kUpdatingUserInformation;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
 
   std::vector<uint8_t> credential_id_bytes;
-  if (!base::HexStringToBytes(args->GetListDeprecated()[1].GetString(),
-                              &credential_id_bytes)) {
+  if (!base::HexStringToBytes(args[1].GetString(), &credential_id_bytes)) {
     NOTREACHED();
   }
   device::PublicKeyCredentialDescriptor credential_id(
       device::CredentialType::kPublicKey, credential_id_bytes);
 
   std::vector<uint8_t> user_handle;
-  if (!base::HexStringToBytes(args->GetListDeprecated()[2].GetString(),
-                              &user_handle)) {
+  if (!base::HexStringToBytes(args[2].GetString(), &user_handle)) {
     NOTREACHED();
   }
-  std::string new_username = args->GetListDeprecated()[3].GetString();
-  std::string new_displayname = args->GetListDeprecated()[4].GetString();
+  std::string new_username = args[3].GetString();
+  std::string new_displayname = args[4].GetString();
 
   device::PublicKeyCredentialUserEntity updated_user(
       std::move(user_handle), std::move(new_username),
@@ -661,15 +660,15 @@
 SecurityKeysBioEnrollmentHandler::~SecurityKeysBioEnrollmentHandler() = default;
 
 void SecurityKeysBioEnrollmentHandler::HandleStart(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(state_, State::kNone);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK(callback_id_.empty());
 
   AllowJavascript();
   state_ = State::kStart;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   bio_ = std::make_unique<device::BioEnrollmentHandler>(
       supported_transports(),
       base::BindOnce(&SecurityKeysBioEnrollmentHandler::OnReady,
@@ -682,41 +681,41 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollStart",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleStart,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollProvidePIN",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleProvidePIN,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollGetSensorInfo",
       base::BindRepeating(
           &SecurityKeysBioEnrollmentHandler::HandleGetSensorInfo,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollEnumerate",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleEnumerate,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollStartEnrolling",
       base::BindRepeating(
           &SecurityKeysBioEnrollmentHandler::HandleStartEnrolling,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollDelete",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleDelete,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollRename",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleRename,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollCancel",
       base::BindRepeating(&SecurityKeysBioEnrollmentHandler::HandleCancel,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyBioEnrollClose",
       base::BindRepeating(
           &HandleClose,
@@ -808,19 +807,19 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleProvidePIN(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(2u, args->GetListDeprecated().size());
+  DCHECK_EQ(2u, args.size());
   DCHECK_EQ(state_, State::kGatherPIN);
   state_ = State::kGatherPIN;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
-  std::move(provide_pin_cb_).Run(args->GetListDeprecated()[1].GetString());
+  callback_id_ = args[0].GetString();
+  std::move(provide_pin_cb_).Run(args[1].GetString());
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleGetSensorInfo(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK_EQ(state_, State::kReady);
   base::DictionaryValue response;
   response.SetIntKey("maxTemplateFriendlyName",
@@ -829,18 +828,17 @@
     response.SetIntKey("maxSamplesForEnroll",
                        *sensor_info_.max_samples_for_enroll);
   }
-  ResolveJavascriptCallback(
-      base::Value(std::move(args->GetListDeprecated()[0].GetString())),
-      std::move(response));
+  ResolveJavascriptCallback(base::Value(std::move(args[0].GetString())),
+                            std::move(response));
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleEnumerate(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK_EQ(state_, State::kReady);
   state_ = State::kEnumerating;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   bio_->EnumerateTemplates(
       base::BindOnce(&SecurityKeysBioEnrollmentHandler::OnHaveEnumeration,
                      weak_factory_.GetWeakPtr()));
@@ -870,12 +868,12 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleStartEnrolling(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
   DCHECK_EQ(state_, State::kReady);
   state_ = State::kEnrolling;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   bio_->EnrollTemplate(
       base::BindRepeating(
           &SecurityKeysBioEnrollmentHandler::OnEnrollingResponse,
@@ -941,14 +939,13 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleDelete(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(2u, args->GetListDeprecated().size());
+  DCHECK_EQ(2u, args.size());
   state_ = State::kDeleting;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   std::vector<uint8_t> template_id;
-  if (!base::HexStringToBytes(args->GetListDeprecated()[1].GetString(),
-                              &template_id)) {
+  if (!base::HexStringToBytes(args[1].GetString(), &template_id)) {
     NOTREACHED();
     return;
   }
@@ -970,19 +967,18 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleRename(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(args->GetListDeprecated().size(), 3u);
+  DCHECK_EQ(args.size(), 3u);
   state_ = State::kRenaming;
-  callback_id_ = args->GetListDeprecated()[0].GetString();
+  callback_id_ = args[0].GetString();
   std::vector<uint8_t> template_id;
-  if (!base::HexStringToBytes(args->GetListDeprecated()[1].GetString(),
-                              &template_id)) {
+  if (!base::HexStringToBytes(args[1].GetString(), &template_id)) {
     NOTREACHED();
     return;
   }
   bio_->RenameTemplate(
-      std::move(template_id), args->GetListDeprecated()[2].GetString(),
+      std::move(template_id), args[2].GetString(),
       base::BindOnce(&SecurityKeysBioEnrollmentHandler::OnRename,
                      weak_factory_.GetWeakPtr()));
 }
@@ -999,10 +995,10 @@
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleCancel(
-    const base::ListValue* args) {
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(state_, State::kEnrolling);
-  DCHECK_EQ(0u, args->GetListDeprecated().size());
+  DCHECK_EQ(0u, args.size());
   DCHECK(!callback_id_.empty());
   // OnEnrollmentFinished() will be invoked once the cancellation is complete.
   bio_->CancelEnrollment();
@@ -1015,35 +1011,35 @@
 void SecurityKeysPhonesHandler::OnJavascriptDisallowed() {}
 
 void SecurityKeysPhonesHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyPhonesEnumerate",
       base::BindRepeating(&SecurityKeysPhonesHandler::HandleEnumerate,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyPhonesDelete",
       base::BindRepeating(&SecurityKeysPhonesHandler::HandleDelete,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "securityKeyPhonesRename",
       base::BindRepeating(&SecurityKeysPhonesHandler::HandleRename,
                           base::Unretained(this)));
 }
 
-void SecurityKeysPhonesHandler::HandleEnumerate(const base::ListValue* args) {
+void SecurityKeysPhonesHandler::HandleEnumerate(
+    base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(1u, args->GetListDeprecated().size());
+  DCHECK_EQ(1u, args.size());
 
   AllowJavascript();
-  DoEnumerate(args->GetListDeprecated()[0]);
+  DoEnumerate(args[0]);
 }
 
-void SecurityKeysPhonesHandler::HandleDelete(const base::ListValue* args) {
+void SecurityKeysPhonesHandler::HandleDelete(base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(2u, args->GetListDeprecated().size());
+  DCHECK_EQ(2u, args.size());
 
   AllowJavascript();
-  const std::string public_key_base64 =
-      args->GetListDeprecated()[1].GetString();
+  const std::string public_key_base64 = args[1].GetString();
   std::array<uint8_t, device::kP256X962Length> public_key;
   const bool ok = DecodePublicKey(public_key_base64, &public_key);
   DCHECK(ok);
@@ -1054,17 +1050,16 @@
           ->GetPrefs();
   cablev2::DeletePairingByPublicKey(prefs, public_key);
 
-  DoEnumerate(args->GetListDeprecated()[0]);
+  DoEnumerate(args[0]);
 }
 
-void SecurityKeysPhonesHandler::HandleRename(const base::ListValue* args) {
+void SecurityKeysPhonesHandler::HandleRename(base::Value::ConstListView args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(3u, args->GetListDeprecated().size());
+  DCHECK_EQ(3u, args.size());
 
   AllowJavascript();
-  const std::string public_key_base64 =
-      args->GetListDeprecated()[1].GetString();
-  const std::string new_name = args->GetListDeprecated()[2].GetString();
+  const std::string public_key_base64 = args[1].GetString();
+  const std::string new_name = args[2].GetString();
   content::BrowserContext* const browser_ctx =
       web_ui()->GetWebContents()->GetBrowserContext();
 
@@ -1092,7 +1087,7 @@
       Profile::FromBrowserContext(browser_ctx)->GetPrefs();
   cablev2::RenamePairing(prefs, public_key, new_name, known_devices->Names());
 
-  ResolveJavascriptCallback(args->GetListDeprecated()[0], base::Value());
+  ResolveJavascriptCallback(args[0], base::Value());
 }
 
 void SecurityKeysPhonesHandler::DoEnumerate(const base::Value& callback_id) {
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.h b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
index 8ac8244..358b6151 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "device/fido/bio/enrollment.h"
 #include "device/fido/bio/enrollment_handler.h"
@@ -17,10 +18,6 @@
 #include "device/fido/fido_discovery_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace base {
-class ListValue;
-}
-
 namespace device {
 struct AggregatedEnumerateCredentialsResponse;
 enum class CredentialManagementStatus;
@@ -79,12 +76,12 @@
   void RegisterMessages() override;
   void Close() override;
 
-  void HandleStartSetPIN(const base::ListValue* args);
+  void HandleStartSetPIN(base::Value::ConstListView args);
   void OnGatherPIN(uint32_t current_min_pin_length,
                    uint32_t new_min_pin_length,
                    absl::optional<int64_t> num_retries);
   void OnSetPINComplete(device::CtapDeviceResponseCode code);
-  void HandleSetPIN(const base::ListValue* args);
+  void HandleSetPIN(base::Value::ConstListView args);
 
   State state_ = State::kNone;
 
@@ -115,9 +112,9 @@
   void RegisterMessages() override;
   void Close() override;
 
-  void HandleReset(const base::ListValue* args);
+  void HandleReset(base::Value::ConstListView args);
   void OnResetSent();
-  void HandleCompleteReset(const base::ListValue* args);
+  void HandleCompleteReset(base::Value::ConstListView args);
   void OnResetFinished(device::CtapDeviceResponseCode result);
 
   State state_ = State::kNone;
@@ -141,9 +138,9 @@
  protected:
   explicit SecurityKeysCredentialHandler(
       std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory);
-  void HandleStart(const base::ListValue* args);
-  void HandlePIN(const base::ListValue* args);
-  void HandleUpdateUserInformation(const base::ListValue* args);
+  void HandleStart(base::Value::ConstListView args);
+  void HandlePIN(base::Value::ConstListView args);
+  void HandleUpdateUserInformation(base::Value::ConstListView args);
 
  private:
   enum class State {
@@ -159,8 +156,8 @@
   void RegisterMessages() override;
   void Close() override;
 
-  void HandleEnumerate(const base::ListValue* args);
-  void HandleDelete(const base::ListValue* args);
+  void HandleEnumerate(base::Value::ConstListView args);
+  void HandleDelete(base::Value::ConstListView args);
 
   void OnCredentialManagementReady();
   void OnHaveCredentials(
@@ -197,9 +194,9 @@
  protected:
   explicit SecurityKeysBioEnrollmentHandler(
       std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory);
-  void HandleStart(const base::ListValue* args);
-  void HandleProvidePIN(const base::ListValue* args);
-  void HandleStartEnrolling(const base::ListValue* args);
+  void HandleStart(base::Value::ConstListView args);
+  void HandleProvidePIN(base::Value::ConstListView args);
+  void HandleStartEnrolling(base::Value::ConstListView args);
 
  private:
   enum class State {
@@ -221,9 +218,9 @@
   void OnGatherPIN(uint32_t min_pin_length,
                    int64_t num_retries,
                    base::OnceCallback<void(std::string)>);
-  void HandleGetSensorInfo(const base::ListValue* args);
+  void HandleGetSensorInfo(base::Value::ConstListView args);
 
-  void HandleEnumerate(const base::ListValue* args);
+  void HandleEnumerate(base::Value::ConstListView args);
   void OnHaveEnumeration(
       device::CtapDeviceResponseCode,
       absl::optional<std::map<std::vector<uint8_t>, std::string>>);
@@ -236,13 +233,13 @@
       device::CtapDeviceResponseCode code,
       absl::optional<std::map<std::vector<uint8_t>, std::string>> enrollments);
 
-  void HandleDelete(const base::ListValue* args);
+  void HandleDelete(base::Value::ConstListView args);
   void OnDelete(device::CtapDeviceResponseCode);
 
-  void HandleRename(const base::ListValue* args);
+  void HandleRename(base::Value::ConstListView args);
   void OnRename(device::CtapDeviceResponseCode);
 
-  void HandleCancel(const base::ListValue* args);
+  void HandleCancel(base::Value::ConstListView args);
 
   State state_ = State::kNone;
   std::string callback_id_;
@@ -263,9 +260,9 @@
   void OnJavascriptDisallowed() override;
 
  private:
-  void HandleEnumerate(const base::ListValue* args);
-  void HandleDelete(const base::ListValue* args);
-  void HandleRename(const base::ListValue* args);
+  void HandleEnumerate(base::Value::ConstListView args);
+  void HandleDelete(base::Value::ConstListView args);
+  void HandleRename(base::Value::ConstListView args);
 
   void DoEnumerate(const base::Value& callback_id);
 };
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
index 1728100..2124042 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
@@ -57,9 +57,9 @@
   // callback.
   std::string SimulateStart() {
     constexpr char kCallbackId[] = "securityKeyCredentialManagementStart";
-    base::ListValue args;
+    base::Value args(base::Value::Type::LIST);
     args.Append(kCallbackId);
-    HandleStart(&args);
+    HandleStart(args.GetList());
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -68,10 +68,10 @@
   // callback.
   std::string SimulateProvidePIN() {
     constexpr char kCallbackId[] = "securityKeyCredentialManagementPIN";
-    base::ListValue args;
+    base::Value args(base::Value::Type::LIST);
     args.Append(kCallbackId);
     args.Append(kTestPIN);
-    HandlePIN(&args);
+    HandlePIN(args.GetList());
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -98,9 +98,9 @@
   // callback.
   std::string SimulateStart() {
     constexpr char kCallbackId[] = "bioEnrollStart";
-    base::ListValue args;
+    base::Value args(base::Value::Type::LIST);
     args.Append(kCallbackId);
-    HandleStart(&args);
+    HandleStart(args.GetList());
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -109,10 +109,10 @@
   // callback.
   std::string SimulateProvidePIN() {
     constexpr char kCallbackId[] = "bioEnrollProvidePIN";
-    base::ListValue args;
+    base::Value args(base::Value::Type::LIST);
     args.Append(kCallbackId);
     args.Append(kTestPIN);
-    HandleProvidePIN(&args);
+    HandleProvidePIN(args.GetList());
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -121,9 +121,9 @@
   // completed callback.
   std::string SimulateStartEnrolling() {
     constexpr char kCallbackId[] = "bioEnrollStartEnrolling";
-    base::ListValue args;
+    base::Value args(base::Value::Type::LIST);
     args.Append(kCallbackId);
-    HandleStartEnrolling(&args);
+    HandleStartEnrolling(args.GetList());
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -197,7 +197,7 @@
   std::string new_username = "jsapple@example.com";
   std::string new_displayname = "John S. Apple";
 
-  base::ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append("securityKeyCredentialManagementUpdate");
   args.Append(credential_id_hex);
   args.Append(user_id_hex);
@@ -212,7 +212,7 @@
   EXPECT_TRUE(*response->FindBoolKey("supportsUpdateUserInformation"));
 
   handler_->SimulateProvidePIN();
-  handler_->HandleUpdateUserInformation(&args);
+  handler_->HandleUpdateUserInformation(args.GetList());
   base::RunLoop().RunUntilIdle();
 
   device::PublicKeyCredentialUserEntity updated_user(
@@ -239,9 +239,9 @@
   handler_->GetDiscoveryFactory()->SetCtap2Config(config);
 
   std::string callback_id("start_callback_id");
-  base::ListValue args;
+  base::Value args(base::Value::Type::LIST);
   args.Append(callback_id);
-  handler_->HandleStart(&args);
+  handler_->HandleStart(args.GetList());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(web_ui_->call_data()[0]->arg1()->GetString(),
diff --git a/chrome/browser/url_param_filter/url_param_filterer.cc b/chrome/browser/url_param_filter/url_param_filterer.cc
index a67ad4a..98c3114 100644
--- a/chrome/browser/url_param_filter/url_param_filterer.cc
+++ b/chrome/browser/url_param_filter/url_param_filterer.cc
@@ -8,6 +8,7 @@
 
 #include "base/base64.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
@@ -19,8 +20,8 @@
 #include "url/gurl.h"
 
 namespace url_param_filter {
-
 namespace {
+
 // Get the ETLD+1 of the URL, which means any subdomain is treated equivalently.
 std::string GetEtldPlusOne(const GURL& gurl) {
   return net::registry_controlled_domains::GetDomainAndRegistry(
@@ -38,12 +39,13 @@
 
 // Filter a given URL according to the passed-in classifications, optionally
 // checking any encoded, nested URLs.
-GURL FilterUrl(const GURL& source_url,
-               const GURL& destination_url,
-               const ClassificationMap& source_classification_map,
-               const ClassificationMap& destination_classification_map,
-               bool check_nested) {
+FilterResult FilterUrl(const GURL& source_url,
+                       const GURL& destination_url,
+                       const ClassificationMap& source_classification_map,
+                       const ClassificationMap& destination_classification_map,
+                       bool check_nested) {
   GURL result = GURL{destination_url};
+  int filtered_params_count = 0;
 
   std::string source_etld_plus1 = GetEtldPlusOne(source_url);
   std::string destination_etld_plus1 = GetEtldPlusOne(destination_url);
@@ -65,7 +67,7 @@
   }
   // Return quickly if there are no parameters we care about.
   if (blocked_parameters.size() == 0) {
-    return result;
+    return FilterResult{destination_url, filtered_params_count};
   }
 
   std::vector<std::string> query_parts;
@@ -79,17 +81,20 @@
       if (check_nested) {
         GURL nested = GURL{net::UnescapeBinaryURLComponent(value)};
         if (nested.is_valid()) {
-          GURL modified =
+          FilterResult nested_result =
               FilterUrl(destination_url, nested, source_classification_map,
                         destination_classification_map, false);
           // If a nested URL contains a param we must filter, do so now.
-          if (nested != modified) {
-            value =
-                net::EscapeQueryParamValue(modified.spec(), /*use_plus=*/false);
+          if (nested != nested_result.filtered_url) {
+            value = net::EscapeQueryParamValue(
+                nested_result.filtered_url.spec(), /*use_plus=*/false);
+            filtered_params_count += nested_result.filtered_param_count;
           }
         }
       }
       query_parts.push_back(base::StrCat({key, "=", value}));
+    } else {
+      filtered_params_count++;
     }
   }
 
@@ -97,7 +102,7 @@
   GURL::Replacements replacements;
   replacements.SetQueryStr(new_query);
   result = result.ReplaceComponents(replacements);
-  return result;
+  return FilterResult{result, filtered_params_count};
 }
 
 url_param_filter::ClassificationMap GetClassifications(
@@ -135,20 +140,31 @@
                                  FilterClassification_SiteRole_DESTINATION));
   return *destination_classifications;
 }
+
+// Write metrics about results of param filtering.
+void WriteMetrics(FilterResult result) {
+  base::UmaHistogramCounts100(
+      "Navigation.UrlParamFilter.FilteredParamCountExperimental",
+      result.filtered_param_count);
+}
 }  // anonymous namespace
 
-GURL FilterUrl(const GURL& source_url,
-               const GURL& destination_url,
-               const ClassificationMap& source_classification_map,
-               const ClassificationMap& destination_classification_map) {
+FilterResult FilterUrl(
+    const GURL& source_url,
+    const GURL& destination_url,
+    const ClassificationMap& source_classification_map,
+    const ClassificationMap& destination_classification_map) {
   return FilterUrl(source_url, destination_url, source_classification_map,
                    destination_classification_map, true);
 }
 
 GURL FilterUrl(const GURL& source_url, const GURL& destination_url) {
   if (base::FeatureList::IsEnabled(features::kIncognitoParamFilterEnabled)) {
-    return FilterUrl(source_url, destination_url, GetSourceClassifications(),
-                     GetDestinationClassifications());
+    FilterResult result =
+        FilterUrl(source_url, destination_url, GetSourceClassifications(),
+                  GetDestinationClassifications());
+    WriteMetrics(result);
+    return result.filtered_url;
   }
   return destination_url;
 }
diff --git a/chrome/browser/url_param_filter/url_param_filterer.h b/chrome/browser/url_param_filter/url_param_filterer.h
index 502f119..1ef7aff 100644
--- a/chrome/browser/url_param_filter/url_param_filterer.h
+++ b/chrome/browser/url_param_filter/url_param_filterer.h
@@ -13,16 +13,25 @@
 using ClassificationMap =
     std::unordered_map<std::string, url_param_filter::FilterClassification>;
 
+// Represents the result of filtering; includes the resulting URL (which may be
+// unmodified), along with the count of params filtered.
+struct FilterResult {
+  GURL filtered_url;
+  int filtered_param_count;
+};
+
 // Filter the destination URL according to the parameter classifications for the
 // source and destination URLs. Used internally by the 2-arg overload, and
-// called directly from tests.
-GURL FilterUrl(const GURL& source_url,
-               const GURL& destination_url,
-               const ClassificationMap& source_classification_map,
-               const ClassificationMap& destination_classification_map);
+// called directly from tests. Defers to caller for any metric writing.
+// Currently experimental; not intended for broad consumption.
+FilterResult FilterUrl(const GURL& source_url,
+                       const GURL& destination_url,
+                       const ClassificationMap& source_classification_map,
+                       const ClassificationMap& destination_classification_map);
 
 // Filter the destination URL according to the default parameter classifications
-// for the source and destination URLs.
+// for the source and destination URLs. Owns its own metric writing.
+// Currently experimental; not intended for broad consumption.
 GURL FilterUrl(const GURL& source_url, const GURL& destination_url);
 
 }  // namespace url_param_filter
diff --git a/chrome/browser/url_param_filter/url_param_filterer_unittest.cc b/chrome/browser/url_param_filter/url_param_filterer_unittest.cc
index 519a1ce..8775948 100644
--- a/chrome/browser/url_param_filter/url_param_filterer_unittest.cc
+++ b/chrome/browser/url_param_filter/url_param_filterer_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/url_param_filter/url_param_filter_classification.pb.h"
 #include "chrome/browser/url_param_filter/url_param_filter_test_helper.h"
@@ -18,10 +19,11 @@
   GURL source = GURL{"http://source.xyz"};
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
   // If no classifications are passed in, don't modify the destination URL.
-  GURL result = url_param_filter::FilterUrl(
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
       source, expected, url_param_filter::ClassificationMap(),
       url_param_filter::ClassificationMap());
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 0);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlNoChanges) {
@@ -40,10 +42,11 @@
 
   // If classifications are passed in, but the destination URL doesn't contain
   // any blocked params, don't modify it.
-  GURL result =
+  url_param_filter::FilterResult result =
       url_param_filter::FilterUrl(source, expected, source_classification_map,
                                   destination_classification_map);
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 0);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlSourceBlocked) {
@@ -58,10 +61,11 @@
   // Navigations from source.xyz with a param called plzblock should have that
   // param removed, regardless of destination.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
       source, destination, source_classification_map,
       url_param_filter::ClassificationMap());
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 1);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlMultipleSourceBlocked) {
@@ -77,10 +81,11 @@
   // Navigations from source.xyz with a param called plzblock or plzblock1
   // should have those params removed, regardless of destination.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
       source, destination, source_classification_map,
       url_param_filter::ClassificationMap());
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlDestinationBlocked) {
@@ -95,10 +100,11 @@
   // Navigations to destination.xyz with a param called plzblock should have
   // that param removed, regardless of source.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
       source, destination, url_param_filter::ClassificationMap(),
       destination_classification_map);
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 1);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlMultipleDestinationBlocked) {
@@ -114,10 +120,11 @@
   // Navigations to destination.xyz with a param called plzblock and/or
   // plzblock1 should have those param removed, regardless of source.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
       source, destination, url_param_filter::ClassificationMap(),
       destination_classification_map);
-  ASSERT_EQ(result, expected);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlSourceAndDestinationBlocked) {
@@ -138,10 +145,11 @@
   // Both source and destination have associated URL param filtering rules. Only
   // nochange should remain.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest,
@@ -166,10 +174,11 @@
   // nochange should remain.
   GURL expected =
       GURL{"https://destination.xyz?nochange=asdf&laternochange=fdsa"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlSubdomainsApplied) {
@@ -189,10 +198,11 @@
               FilterClassification_SiteRole_DESTINATION);
 
   GURL expected = GURL{"https://subdomain.destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlCaseIgnored) {
@@ -212,10 +222,11 @@
 
   // The disallowed params PlZbLoCk and PLZBLOCK1 should be removed.
   GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlWithNestedUrl) {
@@ -243,10 +254,11 @@
       "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
       "3Fnochange%"
       "3Dasdf&nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 2);
 }
 
 TEST_F(UrlParamFiltererTest, FilterUrlWithNestedUrlNotNeedingFiltering) {
@@ -273,10 +285,11 @@
       "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
       "3Fnochange%"
       "3Dasdf&nochange=asdf"};
-  GURL result = url_param_filter::FilterUrl(source, destination,
-                                            source_classification_map,
-                                            destination_classification_map);
-  ASSERT_EQ(result, expected);
+  url_param_filter::FilterResult result = url_param_filter::FilterUrl(
+      source, destination, source_classification_map,
+      destination_classification_map);
+  ASSERT_EQ(result.filtered_url, expected);
+  ASSERT_EQ(result.filtered_param_count, 1);
 }
 
 TEST_F(UrlParamFiltererTest, FeatureDeactivated) {
@@ -309,3 +322,42 @@
 
   ASSERT_EQ(result, expected);
 }
+
+TEST_F(UrlParamFiltererTest, FeatureActivatedMetricsWritten) {
+  base::HistogramTester histograms;
+  const std::string histogram_name =
+      "Navigation.UrlParamFilter.FilteredParamCountExperimental";
+  std::string encoded_classification =
+      url_param_filter::CreateBase64EncodedFilterParamClassificationForTesting(
+          {{"source.xyz", {"plzblock"}}}, {{"destination.xyz", {"plzblock1"}}});
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  // With the flag set, the URL should be filtered.
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kIncognitoParamFilterEnabled,
+      {{"classifications", encoded_classification}});
+
+  GURL source = GURL{"http://source.xyz"};
+  GURL destination =
+      GURL{"https://destination.xyz?plzblock=1&plzblock1=2&nochange=asdf"};
+
+  // The histogram should start off empty.
+  histograms.ExpectTotalCount(histogram_name, 0);
+  url_param_filter::FilterUrl(source, destination);
+  // We filtered two parameters.
+  ASSERT_EQ(histograms.GetTotalSum(histogram_name), 2);
+  url_param_filter::FilterUrl(source, destination);
+  // We filtered two more.
+  ASSERT_EQ(histograms.GetTotalSum(histogram_name), 4);
+  destination = GURL{"https://destination.xyz?plzblock=1&nochange=asdf"};
+  url_param_filter::FilterUrl(source, destination);
+
+  // This time just one more.
+  ASSERT_EQ(histograms.GetTotalSum(histogram_name), 5);
+  destination = GURL{"https://destination.xyz?nochange=asdf"};
+  url_param_filter::FilterUrl(source, destination);
+  // This time we didn't filter any.
+  ASSERT_EQ(histograms.GetTotalSum(histogram_name), 5);
+  // The number of samples should be 4 (four calls to FilterUrl).
+  histograms.ExpectTotalCount(histogram_name, 4);
+}
diff --git a/chrome/browser/web_applications/web_app_utils.cc b/chrome/browser/web_applications/web_app_utils.cc
index b815f0c..2d4b1db4 100644
--- a/chrome/browser/web_applications/web_app_utils.cc
+++ b/chrome/browser/web_applications/web_app_utils.cc
@@ -414,16 +414,23 @@
                                 absl::optional<ApiApprovalState> approval_state,
                                 base::OnceClosure finished_closure) {}
 
-bool HasAppSettingsPage(Profile* profile, const GURL& url) {
+AppId GetAppIdFromAppSettingsUrl(const GURL& url) {
   // App Settings page is served under chrome://app-settings/<app-id>.
   // url.path() returns "/<app-id>" with a leading slash.
   std::string path = url.path();
   if (path.size() <= 1)
+    return AppId();
+  return path.substr(1);
+}
+
+bool HasAppSettingsPage(Profile* profile, const GURL& url) {
+  const AppId app_id = GetAppIdFromAppSettingsUrl(url);
+  if (app_id.empty())
     return false;
+
   WebAppProvider* provider = WebAppProvider::GetForWebApps(profile);
   if (!provider)
     return false;
-  const AppId app_id = path.substr(1);
   return provider->registrar().IsLocallyInstalled(app_id);
 }
 
diff --git a/chrome/browser/web_applications/web_app_utils.h b/chrome/browser/web_applications/web_app_utils.h
index ea40ba1..a895db6 100644
--- a/chrome/browser/web_applications/web_app_utils.h
+++ b/chrome/browser/web_applications/web_app_utils.h
@@ -153,6 +153,9 @@
 // Check if all types of |sources| are uninstallable by the user.
 bool CanUserUninstallWebApp(WebAppSources sources);
 
+// Extracts app_id from chrome://app-settings/<app-id> URL path.
+AppId GetAppIdFromAppSettingsUrl(const GURL& url);
+
 // Check if |url|'s path is an installed web app.
 bool HasAppSettingsPage(Profile* profile, const GURL& url);
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index e5f15b01..c651c6d6 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1643975903-e47b6d379682e12b86d35222ab05da770f25410d.profdata
+chrome-linux-main-1643997497-d0b9bb949302ba9bcb3a3806499eb479335dbf19.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index cf5795ff..316ba6b11 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1643975903-1e7f189ccff1255b140504bb7dae99a3afa78221.profdata
+chrome-mac-arm-main-1643997497-b2d0727f6c71e3485f96539344c4963581193871.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 9fa8f24..bab979a 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1643975903-75717b598e42c8814363eabd1d827dc105438778.profdata
+chrome-mac-main-1643997497-c4546bc27c73f847715efef5b229989aff7c3ed7.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 64e80ce..81bbecb0 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1643986788-6b430427775a73f546733958341f3307e975a013.profdata
+chrome-win32-main-1643997497-c2be50db48a0b1a71639f7122e0e946919b565ad.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 12666faf..aecfe05 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1643986788-b83eccd07a34a10f65132b648c7ade187cd7d611.profdata
+chrome-win64-main-1644008380-a5f7527dbbdebdf9b56255d0991ef81a7359bcbd.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index f6e69536..0320dc9 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -102,6 +102,7 @@
       "$root_gen_dir/components/autofill/core/browser/autofill_address_rewriter_resources.pak",
       "$root_gen_dir/components/components_resources.pak",
       "$root_gen_dir/content/content_resources.pak",
+      "$root_gen_dir/content/quota_internals_resources.pak",
       "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
       "$root_gen_dir/net/net_resources.pak",
       "$root_gen_dir/skia/skia_resources.pak",
@@ -119,6 +120,7 @@
       "//components/autofill/core/browser:autofill_address_rewriter_resources",
       "//components/resources",
       "//content:content_resources",
+      "//content/browser/resources/quota:resources",
       "//mojo/public/js:resources",
       "//net:net_resources",
       "//skia:skia_resources",
diff --git a/chrome/common/extensions/api/web_navigation.json b/chrome/common/extensions/api/web_navigation.json
index 83b4469..1ba0e91 100644
--- a/chrome/common/extensions/api/web_navigation.json
+++ b/chrome/common/extensions/api/web_navigation.json
@@ -61,6 +61,27 @@
                 "parentFrameId": {
                   "type": "integer",
                   "description": "The ID of the parent frame, or <code>-1</code> if this is the main frame."
+                },
+                "documentId": {
+                  "type": "string",
+                  "description": "A UUID of the document loaded.",
+                  "nodoc": true
+                },
+                "parentDocumentId": {
+                  "type": "string",
+                  "optional": true,
+                  "description": "A UUID of the parent document owning this frame. This is not set if there is no parent.",
+                  "nodoc": true
+                },
+                "documentLifecycle": {
+                  "type": "string",
+                  "description": "The lifecycle the document is in.",
+                  "nodoc": true
+                },
+                "frameType": {
+                  "type": "string",
+                  "description": "The type of frame the navigation occurred in.",
+                  "nodoc": true
                 }
               }
             }
@@ -111,6 +132,27 @@
                   "url": {
                     "type": "string",
                     "description": "The URL currently associated with this frame."
+                  },
+                  "documentId": {
+                    "type": "string",
+                    "description": "A UUID of the document loaded.",
+                    "nodoc": true
+                  },
+                  "parentDocumentId": {
+                    "type": "string",
+                    "optional": true,
+                    "description": "A UUID of the parent document owning this frame. This is not set if there is no parent.",
+                    "nodoc": true
+                  },
+                  "documentLifecycle": {
+                    "type": "string",
+                    "description": "The lifecycle the document is in.",
+                    "nodoc": true
+                  },
+                  "frameType": {
+                    "type": "string",
+                    "description": "The type of frame the navigation occurred in.",
+                    "nodoc": true
                   }
                 }
               }
diff --git a/chrome/test/data/extensions/api_test/webnavigation/fencedFrames/test_fencedFrame.js b/chrome/test/data/extensions/api_test/webnavigation/fencedFrames/test_fencedFrame.js
index dfb810f..6c5791f 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/fencedFrames/test_fencedFrame.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/fencedFrames/test_fencedFrame.js
@@ -176,20 +176,45 @@
 
     function testGetAllFrames() {
       chrome.webNavigation.getAllFrames({tabId: tab.id}, function (details) {
-          // Since processIds are randomly assigned we remove them for the
-          // assertEq.
-          details.forEach(element => delete element.processId);
+          var documentIds = [];
+          var nextDocumentId = 1;
+          details.forEach(element => {
+            // Since processIds are randomly assigned we remove them for the
+            // assertEq.
+            delete element.processId;
+            if ('parentDocumentId' in element) {
+              if (documentIds[element.parentDocumentId] === undefined) {
+                documentIds[element.parentDocumentId] = nextDocumentId++;
+              }
+              element.parentDocumentId = documentIds[element.parentDocumentId];
+            }
+            if (documentIds[element.documentId] === undefined) {
+              documentIds[element.documentId] = nextDocumentId++;
+            }
+            element.documentId = documentIds[element.documentId];
+          });
           chrome.test.assertEq(
               [{errorOccurred: false,
+                documentId: 1,
+                documentLifecycle: "active",
                 frameId: 0,
+                frameType: "outermost_frame",
                 parentFrameId: -1,
                 url: URL_MAIN},
               {errorOccurred: false,
+                documentId: 2,
+                documentLifecycle: "active",
                 frameId: 4,
+                frameType: "sub_frame",
+                parentDocumentId: 1,
                 parentFrameId: 0,
                 url: URL_INTERMEDIATE_IFRAME},
               {errorOccurred: false,
-               frameId: mparchEnabled ? 6 : 5,
+                documentId: 3,
+                documentLifecycle: "active",
+                frameId: mparchEnabled ? 6 : 5,
+                frameType: "fenced_frame",
+                parentDocumentId: 2,
                 parentFrameId: 4,
                 url: URL_FENCED_FRAME}],
                details);
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
index e0dc369..9a3ead65 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
@@ -20,6 +20,7 @@
   var URL = chrome.extension.getURL("a.html");
   var URL_FRAMES = chrome.extension.getURL("b.html");
   var processId = -1;
+  var documentId;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
   chrome.test.runTests([
@@ -29,11 +30,18 @@
           if (details.tabId != tab.id || details.url != URL)
             return;
           processId = details.processId;
+          documentId = details.documentId;
           chrome.webNavigation.getFrame(
-              {tabId: tab.id, frameId: 0, processId: processId},
+              {frameId: 0, tabId: tab.id, processId: processId},
               function(details) {
             chrome.test.assertEq(
-                {errorOccurred: false, url: URL, parentFrameId: -1},
+                {errorOccurred: false,
+                 url: URL,
+                 parentFrameId: -1,
+                 documentId: documentId,
+                 documentLifecycle: "active",
+                 frameType: "outermost_frame",
+               },
                 details);
             done();
           });
@@ -57,7 +65,10 @@
                 frameId: 0,
                 parentFrameId: -1,
                 processId: processId,
-                url: URL}],
+                url: URL,
+                documentId: documentId,
+                documentLifecycle: "active",
+                frameType: "outermost_frame"}],
                details);
           chrome.test.succeed();
       });
@@ -78,6 +89,7 @@
           if (details.tabId != tab.id || details.url != URL_FRAMES)
             return;
           processId = details.processId;
+          documentId = details.documentId;
           chrome.webNavigation.getAllFrames(
               {tabId: tab.id},
             function (details) {
@@ -86,7 +98,10 @@
                     frameId: 0,
                     parentFrameId: -1,
                     processId: processId,
-                    url: URL_FRAMES}],
+                    url: URL_FRAMES,
+                    documentId: documentId,
+                    documentLifecycle: "active",
+                    frameType: "outermost_frame"}],
                    details);
               chrome.test.succeed();
           });
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 720cb5b..0a35f78 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -617,6 +617,7 @@
     "cr_elements:build_grdp",
     "downloads:build_grdp",
     "history:build_grdp",
+    "new_tab_page:build_grdp",
     "read_later:build_grdp",
     "settings:build_grdp",
     "tab_search:build_grdp",
@@ -629,6 +630,7 @@
     "$target_gen_dir/cr_elements/resources.grdp",
     "$target_gen_dir/downloads/resources.grdp",
     "$target_gen_dir/history/resources.grdp",
+    "$target_gen_dir/new_tab_page/resources.grdp",
     "$target_gen_dir/read_later/resources.grdp",
     "$target_gen_dir/settings/resources.grdp",
     "$target_gen_dir/tab_search/resources.grdp",
diff --git a/chrome/test/data/webui/history/BUILD.gn b/chrome/test/data/webui/history/BUILD.gn
index c89355f3..1c88cae 100644
--- a/chrome/test/data/webui/history/BUILD.gn
+++ b/chrome/test/data/webui/history/BUILD.gn
@@ -7,23 +7,23 @@
 
 # Test files that do not require preprocessing.
 non_preprocessed_files = [
-  "history_clusters/utils.js",
+  "history_clusters/utils.ts",
   "history_drawer_test.js",
   "history_item_focus_test.js",
   "history_item_test.js",
   "history_list_focus_test.js",
   "history_list_test.js",
-  "history_metrics_test.js",
+  "history_metrics_test.ts",
   "history_overflow_menu_test.js",
-  "history_routing_test.js",
-  "history_routing_with_query_param_test.js",
+  "history_routing_test.ts",
+  "history_routing_with_query_param_test.ts",
   "history_supervised_user_test.js",
   "history_synced_device_manager_focus_test.js",
   "history_synced_tabs_test.js",
-  "history_toolbar_focus_test.js",
-  "history_toolbar_test.js",
-  "link_click_test.js",
-  "searched_label_test.js",
+  "history_toolbar_focus_test.ts",
+  "history_toolbar_test.ts",
+  "link_click_test.ts",
+  "searched_label_test.ts",
   "test_browser_service.ts",
   "test_util.ts",
 ]
diff --git a/chrome/test/data/webui/history/history_clusters/utils.js b/chrome/test/data/webui/history/history_clusters/utils.js
deleted file mode 100644
index 0984e87..0000000
--- a/chrome/test/data/webui/history/history_clusters/utils.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2021 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 {PageCallbackRouter, PageHandlerRemote} from 'chrome://history/history.js';
-import {TestBrowserProxy as BaseTestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-export class TestBrowserProxy extends BaseTestBrowserProxy {
-  constructor() {
-    super([]);
-    this.handler = TestBrowserProxy.fromClass(PageHandlerRemote);
-    this.callbackRouter = new PageCallbackRouter();
-  }
-}
-
-export class TestMetricsProxy extends BaseTestBrowserProxy {
-  constructor() {
-    super(['recordToggledVisibility']);
-  }
-
-  recordToggledVisibility(visible) {
-    this.methodCalled('recordToggledVisibility', visible);
-  }
-}
diff --git a/chrome/test/data/webui/history/history_clusters/utils.ts b/chrome/test/data/webui/history/history_clusters/utils.ts
new file mode 100644
index 0000000..39c9a72
--- /dev/null
+++ b/chrome/test/data/webui/history/history_clusters/utils.ts
@@ -0,0 +1,45 @@
+// Copyright 2021 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 {ClusterAction, MetricsProxy, PageCallbackRouter, PageHandlerRemote, RelatedSearchAction, VisitAction, VisitType} from 'chrome://history/history.js';
+import {TestBrowserProxy as BaseTestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+export class TestBrowserProxy extends BaseTestBrowserProxy {
+  handler: PageHandlerRemote&BaseTestBrowserProxy;
+  callbackRouter: PageCallbackRouter;
+
+  constructor() {
+    super([]);
+    this.handler = BaseTestBrowserProxy.fromClass(PageHandlerRemote);
+    this.callbackRouter = new PageCallbackRouter();
+  }
+}
+
+export class TestMetricsProxy extends BaseTestBrowserProxy implements
+    MetricsProxy {
+  constructor() {
+    super([
+      'recordClusterAction',
+      'recordRelatedSearchAction',
+      'recordToggledVisibility',
+      'recordVisitAction',
+    ]);
+  }
+
+  recordClusterAction(action: ClusterAction, index: number) {
+    this.methodCalled('recordClusterAction', [action, index]);
+  }
+
+  recordRelatedSearchAction(action: RelatedSearchAction, index: number) {
+    this.methodCalled('recordRelatedSearchAction', [action, index]);
+  }
+
+  recordToggledVisibility(visible: boolean) {
+    this.methodCalled('recordToggledVisibility', visible);
+  }
+
+  recordVisitAction(action: VisitAction, index: number, type: VisitType) {
+    this.methodCalled('recordVisitAction', [action, index, type]);
+  }
+}
diff --git a/chrome/test/data/webui/history/history_drawer_test.js b/chrome/test/data/webui/history/history_drawer_test.js
index cd3982b..ee3b3b0 100644
--- a/chrome/test/data/webui/history/history_drawer_test.js
+++ b/chrome/test/data/webui/history/history_drawer_test.js
@@ -35,8 +35,7 @@
       assertFalse(!!drawerSideBar);
 
       const menuButton =
-          app.$.toolbar.$['main-toolbar'].shadowRoot.querySelector(
-              '#menuButton');
+          app.$.toolbar.$.mainToolbar.shadowRoot.querySelector('#menuButton');
       assertTrue(!!menuButton);
 
       menuButton.click();
diff --git a/chrome/test/data/webui/history/history_item_test.js b/chrome/test/data/webui/history/history_item_test.js
index e710b13..14362b6 100644
--- a/chrome/test/data/webui/history/history_item_test.js
+++ b/chrome/test/data/webui/history/history_item_test.js
@@ -81,6 +81,10 @@
     return testService.whenCalled('queryHistory');
   });
 
+  function getHistoryData() {
+    return element.$['infinite-list'].items;
+  }
+
   test('basic separator insertion', function() {
     element.addNewResults(TEST_HISTORY_RESULTS);
     return flushTasks().then(function() {
@@ -118,7 +122,7 @@
       const items = element.shadowRoot.querySelectorAll('history-item');
 
       element.removeItemsByIndex_([3]);
-      assertEquals(5, element.historyData_.length);
+      assertEquals(5, getHistoryData().length);
 
       // Checks that a new time gap separator has been inserted.
       assertTrue(items[2].hasTimeGap);
@@ -145,8 +149,8 @@
           items[1].$$('#bookmark-star').click();
 
           // Check that all items matching this url are unstarred.
-          assertEquals(element.historyData_[1].starred, false);
-          assertEquals(element.historyData_[5].starred, false);
+          assertEquals(getHistoryData()[1].starred, false);
+          assertEquals(getHistoryData()[5].starred, false);
         });
   });
 });
diff --git a/chrome/test/data/webui/history/history_list_focus_test.js b/chrome/test/data/webui/history/history_list_focus_test.js
index 2b20892..77ba24db 100644
--- a/chrome/test/data/webui/history/history_list_focus_test.js
+++ b/chrome/test/data/webui/history/history_list_focus_test.js
@@ -4,6 +4,7 @@
 
 import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {isMac} from 'chrome://resources/js/cr.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
@@ -43,11 +44,15 @@
         ])
         .then(flushTasks)
         .then(function() {
-          element.$$('iron-list').fire('iron-resize');
+          element.$['infinite-list'].fire('iron-resize');
           return waitAfterNextRender(element);
         });
   });
 
+  function getHistoryData() {
+    return element.$['infinite-list'].items;
+  }
+
   test('list focus and keyboard nav', async () => {
     let focused;
     await flushTasks();
@@ -64,49 +69,49 @@
     pressAndReleaseKeyOn(focused, 39, [], 'ArrowRight');
     flush();
     focused = items[2].$.link;
-    assertEquals(focused, element.lastFocused_);
+    assertEquals(focused, getDeepActiveElement());
     assertTrue(items[2].row_.isActive());
     assertFalse(items[3].row_.isActive());
 
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     flush();
     focused = items[3].$.link;
-    assertEquals(focused, element.lastFocused_);
+    assertEquals(focused, getDeepActiveElement());
     assertFalse(items[2].row_.isActive());
     assertTrue(items[3].row_.isActive());
 
     pressAndReleaseKeyOn(focused, 39, [], 'ArrowRight');
     flush();
     focused = items[3].$['menu-button'];
-    assertEquals(focused, element.lastFocused_);
+    assertEquals(focused, getDeepActiveElement());
     assertFalse(items[2].row_.isActive());
     assertTrue(items[3].row_.isActive());
 
     pressAndReleaseKeyOn(focused, 38, [], 'ArrowUp');
     flush();
     focused = items[2].$['menu-button'];
-    assertEquals(focused, element.lastFocused_);
+    assertEquals(focused, getDeepActiveElement());
     assertTrue(items[2].row_.isActive());
     assertFalse(items[3].row_.isActive());
 
     pressAndReleaseKeyOn(focused, 37, [], 'ArrowLeft');
     flush();
-    focused = items[2].$$('#bookmark-star');
-    assertEquals(focused, element.lastFocused_);
+    focused = items[2].shadowRoot.querySelector('#bookmark-star');
+    assertEquals(focused, getDeepActiveElement());
     assertTrue(items[2].row_.isActive());
     assertFalse(items[3].row_.isActive());
 
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     flush();
     focused = items[3].$.link;
-    assertEquals(focused, element.lastFocused_);
+    assertEquals(focused, getDeepActiveElement());
     assertFalse(items[2].row_.isActive());
     assertTrue(items[3].row_.isActive());
   });
 
   test('selection of all items using ctrl + a', async () => {
     const toolbar = app.$.toolbar;
-    const field = toolbar.$['main-toolbar'].getSearchField();
+    const field = toolbar.$.mainToolbar.getSearchField();
     field.blur();
     assertFalse(field.showingSearch);
 
@@ -117,14 +122,13 @@
     assertTrue(keydownEvent.defaultPrevented);
 
     assertDeepEquals(
-        [true, true, true, true], element.historyData_.map(i => i.selected));
+        [true, true, true, true], getHistoryData().map(i => i.selected));
 
     // If everything is already selected, the same shortcut will trigger
     // cancelling selection.
     pressAndReleaseKeyOn(document.body, 65, modifier, 'a');
     assertDeepEquals(
-        [false, false, false, false],
-        element.historyData_.map(i => i.selected));
+        [false, false, false, false], getHistoryData().map(i => i.selected));
 
     // If the search field is focused, the keyboard event should be handled by
     // the browser (which triggers selection of the text within the search
@@ -135,8 +139,7 @@
     keydownEvent = await promise;
     assertFalse(keydownEvent.defaultPrevented);
     assertDeepEquals(
-        [false, false, false, false],
-        element.historyData_.map(i => i.selected));
+        [false, false, false, false], getHistoryData().map(i => i.selected));
   });
 
   test('deleting last item will focus on new last item', async () => {
@@ -144,15 +147,15 @@
     await flushTasks();
     flush();
     const items = polymerSelectAll(element, 'history-item');
-    assertEquals(4, element.historyData_.length);
+    assertEquals(4, getHistoryData().length);
     assertEquals(4, items.length);
     items[3].$['menu-button'].click();
     await flushTasks();
-    element.$$('#menuRemoveButton').click();
-    assertNotEquals(items[2].$['menu-button'], element.lastFocused_);
+    element.shadowRoot.querySelector('#menuRemoveButton').click();
+    assertNotEquals(items[2].$['menu-button'], getDeepActiveElement());
     await testService.whenCalled('removeVisits');
     await flushTasks();
-    assertEquals(3, element.historyData_.length);
-    assertEquals(items[2].$['menu-button'], element.lastFocused_);
+    assertEquals(3, getHistoryData().length);
+    assertEquals(items[2].$['menu-button'], getDeepActiveElement());
   });
 });
diff --git a/chrome/test/data/webui/history/history_list_test.js b/chrome/test/data/webui/history/history_list_test.js
index 600288a..fb0ac2d 100644
--- a/chrome/test/data/webui/history/history_list_test.js
+++ b/chrome/test/data/webui/history/history_list_test.js
@@ -96,17 +96,21 @@
     ]);
   }
 
+  function getHistoryData() {
+    return element.$['infinite-list'].items;
+  }
+
   test(history_list_test.TestNames.DeletingSingleItem, function() {
     return finishSetup([createHistoryEntry('2015-01-01', 'http://example.com')])
         .then(flushTasks)
         .then(function() {
-          assertEquals(element.historyData_.length, 1);
+          assertEquals(getHistoryData().length, 1);
           flush();
           const items = polymerSelectAll(element, 'history-item');
 
           assertEquals(1, items.length);
           items[0].$.checkbox.click();
-          assertDeepEquals([true], element.historyData_.map(i => i.selected));
+          assertDeepEquals([true], getHistoryData().map(i => i.selected));
           return flushTasks();
         })
         .then(function() {
@@ -153,7 +157,7 @@
               // items.
               assertDeepEquals(
                   [false, false, true, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
 
               toolbar.clearSelectedItems();
 
@@ -161,7 +165,7 @@
               // and the actual history-items affected.
               assertDeepEquals(
                   [false, false, false, false],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
 
               assertFalse(items[2].selected);
               assertFalse(items[3].selected);
@@ -184,14 +188,14 @@
               items[1].$.checkbox.click();
               assertDeepEquals(
                   [false, true, false, false],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals([1], Array.from(element.selectedItems).sort());
 
               // Shift-select to the last item.
               shiftClick(items[3].$.checkbox);
               assertDeepEquals(
                   [false, true, true, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals(
                   [1, 2, 3], Array.from(element.selectedItems).sort());
 
@@ -199,7 +203,7 @@
               shiftClick(items[0].$.checkbox);
               assertDeepEquals(
                   [true, true, true, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals(
                   [0, 1, 2, 3], Array.from(element.selectedItems).sort());
 
@@ -207,14 +211,14 @@
               shiftClick(items[2].$.checkbox);
               assertDeepEquals(
                   [false, false, false, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals([3], Array.from(element.selectedItems).sort());
 
               // Select the second item.
               items[1].$.checkbox.click();
               assertDeepEquals(
                   [false, true, false, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals(
                   [1, 3], Array.from(element.selectedItems).sort());
 
@@ -222,14 +226,14 @@
               shiftClick(items[3].$.checkbox);
               assertDeepEquals(
                   [false, false, false, false],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals([], Array.from(element.selectedItems).sort());
 
               // Shift-select back to the third item.
               shiftClick(items[2].$.checkbox);
               assertDeepEquals(
                   [false, false, true, true],
-                  element.historyData_.map(i => i.selected));
+                  getHistoryData().map(i => i.selected));
               assertDeepEquals(
                   [2, 3], Array.from(element.selectedItems).sort());
 
@@ -237,7 +241,7 @@
               element.removeItemsByIndex_(Array.from(element.selectedItems));
               assertDeepEquals(
                   ['https://www.google.com', 'https://www.example.com'],
-                  element.historyData_.map(i => i.title));
+                  getHistoryData().map(i => i.title));
             });
       });
 
@@ -249,7 +253,7 @@
           return flushTasks();
         })
         .then(function() {
-          const field = toolbar.$['main-toolbar'].getSearchField();
+          const field = toolbar.$.mainToolbar.getSearchField();
           field.blur();
           assertFalse(field.showingSearch);
 
@@ -258,7 +262,7 @@
 
           assertDeepEquals(
               [false, false, false, false],
-              element.historyData_.map(i => i.selected));
+              getHistoryData().map(i => i.selected));
         });
   });
 
@@ -329,10 +333,11 @@
           flush();
           const items = polymerSelectAll(element, 'history-item');
 
-          assertEquals(element.historyData_.length, 5);
-          assertEquals(element.historyData_[0].dateRelativeDay, '2016-03-15');
-          assertEquals(element.historyData_[2].dateRelativeDay, '2016-03-13');
-          assertEquals(element.historyData_[4].dateRelativeDay, '2016-03-11');
+          const historyData = getHistoryData();
+          assertEquals(historyData.length, 5);
+          assertEquals(historyData[0].dateRelativeDay, '2016-03-15');
+          assertEquals(historyData[2].dateRelativeDay, '2016-03-13');
+          assertEquals(historyData[4].dateRelativeDay, '2016-03-11');
 
           // Checks that the first and last items have been reset correctly.
           assertTrue(items[2].isCardStart);
@@ -423,7 +428,7 @@
             .then(function() {
               assertEquals(
                   'www.google.com',
-                  toolbar.$['main-toolbar'].getSearchField().getValue());
+                  toolbar.$.mainToolbar.getSearchField().getValue());
 
               element.$.sharedMenu.get().close();
               items[0].$['menu-button'].click();
@@ -551,10 +556,11 @@
         })
         .then(flushTasks)
         .then(function() {
-          assertEquals(5, element.historyData_.length);
-          assertEquals(element.historyData_[0].dateRelativeDay, '2016-03-15');
-          assertEquals(element.historyData_[2].dateRelativeDay, '2016-03-13');
-          assertEquals(element.historyData_[4].dateRelativeDay, '2016-03-11');
+          const historyData = getHistoryData();
+          assertEquals(5, historyData.length);
+          assertEquals(historyData[0].dateRelativeDay, '2016-03-15');
+          assertEquals(historyData[2].dateRelativeDay, '2016-03-13');
+          assertEquals(historyData[4].dateRelativeDay, '2016-03-11');
           assertFalse(dialog.open);
 
           flush();
@@ -603,7 +609,7 @@
                 'https://www.google.com',
                 'https://en.wikipedia.org',
               ],
-              element.historyData_.map(item => item.title));
+              getHistoryData().map(item => item.title));
 
           // Deletion should deselect all.
           assertDeepEquals(
diff --git a/chrome/test/data/webui/history/history_metrics_test.js b/chrome/test/data/webui/history/history_metrics_test.js
deleted file mode 100644
index de9eba46..0000000
--- a/chrome/test/data/webui/history/history_metrics_test.js
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright 2016 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 {BrowserServiceImpl, ensureLazyLoaded, HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from 'chrome://history/history.js';
-import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
-
-import {TestBrowserService} from './test_browser_service.js';
-import {createHistoryEntry, createHistoryInfo, createSession, createWindow, disableLinkClicks, polymerSelectAll} from './test_util.js';
-
-suite('Metrics', function() {
-  let testService;
-  let app;
-  let histogramMap;
-  let actionMap;
-
-  suiteSetup(function() {
-    disableLinkClicks();
-  });
-
-  setup(async () => {
-    document.body.innerHTML = '';
-
-    BrowserServiceImpl.setInstance(new TestBrowserService());
-    testService = BrowserServiceImpl.getInstance();
-
-    actionMap = testService.actionMap;
-    histogramMap = testService.histogramMap;
-
-    app = document.createElement('history-app');
-  });
-
-  /**
-   * @param {!Array<!HistoryEntry>} queryResults The query results to initialize
-   *     the page with.
-   * @param {string=} query The query to use in the QueryInfo.
-   * @return {!Promise} Promise that resolves when initialization is complete.
-   */
-  function finishSetup(queryResults, query) {
-    testService.setQueryResult(
-        {info: createHistoryInfo(query), value: queryResults});
-    document.body.appendChild(app);
-    return Promise
-        .all([
-          testService.whenCalled('queryHistory'),
-          ensureLazyLoaded(),
-        ])
-        .then(function() {
-          webUIListenerCallback('sign-in-state-changed', false);
-          return flushTasks();
-        });
-  }
-
-
-  test('History.HistoryPageView', async () => {
-    await finishSetup([]);
-    app.grouped_ = true;
-
-    const histogram = histogramMap['History.HistoryPageView'];
-    assertEquals(1, histogram[HistoryPageViewHistogram.HISTORY]);
-
-    app.selectedPage_ = 'syncedTabs';
-    assertEquals(1, histogram[HistoryPageViewHistogram.SIGNIN_PROMO]);
-    await testService.whenCalled('otherDevicesInitialized');
-
-    testService.resetResolver('recordHistogram');
-    webUIListenerCallback('sign-in-state-changed', true);
-    await testService.whenCalled('recordHistogram');
-
-    assertEquals(1, histogram[HistoryPageViewHistogram.SYNCED_TABS]);
-    app.selectedPage_ = 'history';
-    assertEquals(2, histogram[HistoryPageViewHistogram.HISTORY]);
-  });
-
-  test('history-list', async () => {
-    // Create a history entry that is between 7 and 8 days in the past. For the
-    // purposes of the tested functionality, we consider a day to be a 24 hour
-    // period, with no regard to DST shifts.
-    const weekAgo =
-        new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000 - 1);
-
-    const historyEntry = createHistoryEntry(weekAgo, 'http://www.google.com');
-    historyEntry.starred = true;
-    await finishSetup(
-        [createHistoryEntry(weekAgo, 'http://www.example.com'), historyEntry]);
-    await flushTasks();
-
-    let items = polymerSelectAll(app.$.history, 'history-item');
-    items[1].$$('#bookmark-star').click();
-    assertEquals(1, actionMap['BookmarkStarClicked']);
-    items[1].$.link.click();
-    assertEquals(1, actionMap['EntryLinkClick']);
-    assertEquals(1, histogramMap['HistoryPage.ClickPosition'][1]);
-    assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset'][1]);
-
-    // TODO(https://crbug.com/1000573): Log the contents of this histogram
-    // for debugging in case the flakiness reoccurs.
-    console.log(Object.keys(histogramMap['HistoryPage.ClickAgeInDays']));
-
-    // The "age in days" histogram should record 8 days, since the history
-    // entry was created between 7 and 8 days ago and we round the
-    // recorded value up.
-    assertEquals(1, histogramMap['HistoryPage.ClickAgeInDays'][8]);
-    assertEquals(1, histogramMap['HistoryPage.ClickAgeInDaysSubset'][8]);
-
-    testService.resetResolver('queryHistory');
-    testService.setQueryResult({
-      info: createHistoryInfo('goog'),
-      value: [
-        createHistoryEntry(weekAgo, 'http://www.google.com'),
-        createHistoryEntry(weekAgo, 'http://www.google.com'),
-        createHistoryEntry(weekAgo, 'http://www.google.com'),
-      ],
-    });
-    app.fire('change-query', {search: 'goog'});
-    assertEquals(1, actionMap['Search']);
-    app.set('queryState_.incremental', true);
-    await Promise.all([
-      testService.whenCalled('queryHistory'),
-      flushTasks(),
-    ]);
-
-    app.$.history.$$('iron-list').fire('iron-resize');
-    await waitAfterNextRender(app.$.history);
-    flush();
-
-    items = polymerSelectAll(app.$.history, 'history-item');
-    items[0].$.link.click();
-    assertEquals(1, actionMap['SearchResultClick']);
-    assertEquals(1, histogramMap['HistoryPage.ClickPosition'][0]);
-    assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset'][0]);
-    items[0].$.checkbox.click();
-    items[4].$.checkbox.click();
-    await flushTasks();
-
-    app.$.toolbar.deleteSelectedItems();
-    assertEquals(1, actionMap['RemoveSelected']);
-    await flushTasks();
-
-    app.$.history.$$('.cancel-button').click();
-    assertEquals(1, actionMap['CancelRemoveSelected']);
-    app.$.toolbar.deleteSelectedItems();
-    await flushTasks();
-
-    app.$.history.$$('.action-button').click();
-    assertEquals(1, actionMap['ConfirmRemoveSelected']);
-    await flushTasks();
-
-    items = polymerSelectAll(app.$.history, 'history-item');
-    items[0].$['menu-button'].click();
-    await flushTasks();
-
-    app.$.history.$$('#menuRemoveButton').click();
-    await Promise.all([
-      testService.whenCalled('removeVisits'),
-      flushTasks(),
-    ]);
-
-    assertEquals(1, histogramMap['HistoryPage.RemoveEntryPosition'][0]);
-    assertEquals(1, histogramMap['HistoryPage.RemoveEntryPositionSubset'][0]);
-  });
-
-  test('synced-device-manager', async () => {
-    const sessionList = [
-      createSession(
-          'Nexus 5',
-          [createWindow(['http://www.google.com', 'http://example.com'])]),
-      createSession(
-          'Nexus 6',
-          [
-            createWindow(['http://test.com']),
-            createWindow(['http://www.gmail.com', 'http://badssl.com'])
-          ]),
-    ];
-    testService.setForeignSessions(sessionList);
-    await finishSetup([]);
-
-    app.selectedPage_ = 'syncedTabs';
-    await flushTasks();
-
-    const histogram = histogramMap[SYNCED_TABS_HISTOGRAM_NAME];
-    assertEquals(1, histogram[SyncedTabsHistogram.INITIALIZED]);
-
-    await testService.whenCalled('getForeignSessions');
-    await flushTasks();
-
-    assertEquals(1, histogram[SyncedTabsHistogram.HAS_FOREIGN_DATA]);
-    await flushTasks();
-
-    const cards = polymerSelectAll(
-        app.$$('#synced-devices'), 'history-synced-device-card');
-    cards[0].$['card-heading'].click();
-    assertEquals(1, histogram[SyncedTabsHistogram.COLLAPSE_SESSION]);
-    cards[0].$['card-heading'].click();
-    assertEquals(1, histogram[SyncedTabsHistogram.EXPAND_SESSION]);
-    polymerSelectAll(cards[0], '.website-link')[0].click();
-    assertEquals(1, histogram[SyncedTabsHistogram.LINK_CLICKED]);
-
-    const menuButton = cards[0].$['menu-button'];
-    menuButton.click();
-    await flushTasks();
-
-    app.$$('#synced-devices').$$('#menuOpenButton').click();
-    assertEquals(1, histogram[SyncedTabsHistogram.OPEN_ALL]);
-
-    menuButton.click();
-    await flushTasks();
-
-    app.$$('#synced-devices').$$('#menuDeleteButton').click();
-    assertEquals(1, histogram[SyncedTabsHistogram.HIDE_FOR_NOW]);
-  });
-});
diff --git a/chrome/test/data/webui/history/history_metrics_test.ts b/chrome/test/data/webui/history/history_metrics_test.ts
new file mode 100644
index 0000000..5798cb0
--- /dev/null
+++ b/chrome/test/data/webui/history/history_metrics_test.ts
@@ -0,0 +1,246 @@
+// Copyright 2016 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 'chrome://history/history.js';
+import 'chrome://history/lazy_load.js';
+
+import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from 'chrome://history/history.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+
+import {TestBrowserService} from './test_browser_service.js';
+import {createHistoryEntry, createHistoryInfo, createSession, createWindow, disableLinkClicks} from './test_util.js';
+
+suite('Metrics', function() {
+  let testService: TestBrowserService;
+  let app: HistoryAppElement;
+  let histogramMap: {[key: string]: {[key: string]: number}};
+  let actionMap: {[key: string]: number};
+
+  suiteSetup(function() {
+    disableLinkClicks();
+  });
+
+  setup(async () => {
+    document.body.innerHTML = '';
+
+    testService = new TestBrowserService();
+    BrowserServiceImpl.setInstance(testService);
+
+    actionMap = testService.actionMap;
+    histogramMap = testService.histogramMap;
+
+    app = document.createElement('history-app');
+  });
+
+  /**
+   * @param queryResults The query results to initialize the page with.
+   * @param query The query to use in the QueryInfo.
+   * @return Promise that resolves when initialization is complete.
+   */
+  function finishSetup(
+      queryResults: HistoryEntry[], query?: string): Promise<void> {
+    testService.setQueryResult(
+        {info: createHistoryInfo(query), value: queryResults});
+    document.body.appendChild(app);
+    return Promise
+        .all([
+          testService.whenCalled('queryHistory'),
+          ensureLazyLoaded(),
+        ])
+        .then(function() {
+          webUIListenerCallback('sign-in-state-changed', false);
+          return flushTasks();
+        });
+  }
+
+  function navigateTo(route: string) {
+    window.history.replaceState({}, '', route);
+    window.dispatchEvent(new CustomEvent('location-changed'));
+    // Update from the URL synchronously.
+    app.shadowRoot!.querySelector(
+                       'history-router')!.getDebouncerForTesting()!.flush();
+  }
+
+  test('History.HistoryPageView', async () => {
+    await finishSetup([]);
+
+    const histogram = histogramMap['History.HistoryPageView'];
+    assertTrue(!!histogram);
+    assertEquals(1, histogram[HistoryPageViewHistogram.HISTORY]);
+
+    navigateTo('/syncedTabs');
+    assertEquals(1, histogram[HistoryPageViewHistogram.SIGNIN_PROMO]);
+    await testService.whenCalled('otherDevicesInitialized');
+
+    testService.resetResolver('recordHistogram');
+    webUIListenerCallback('sign-in-state-changed', true);
+    await testService.whenCalled('recordHistogram');
+
+    assertEquals(1, histogram[HistoryPageViewHistogram.SYNCED_TABS]);
+    navigateTo('/history');
+    assertEquals(2, histogram[HistoryPageViewHistogram.HISTORY]);
+  });
+
+  test('history-list', async () => {
+    // Create a history entry that is between 7 and 8 days in the past. For the
+    // purposes of the tested functionality, we consider a day to be a 24 hour
+    // period, with no regard to DST shifts.
+    const weekAgo =
+        new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000 - 1);
+
+    const historyEntry =
+        createHistoryEntry(weekAgo.getTime(), 'http://www.google.com');
+    historyEntry.starred = true;
+    await finishSetup([
+      createHistoryEntry(weekAgo.getTime(), 'http://www.example.com'),
+      historyEntry
+    ]);
+    await flushTasks();
+
+    let items = app.$.history.shadowRoot!.querySelectorAll('history-item');
+    assertTrue(!!items[1]);
+    items[1].shadowRoot!.querySelector<HTMLElement>('#bookmark-star')!.click();
+    assertEquals(1, actionMap['BookmarkStarClicked']);
+    items[1].$.link.click();
+    assertEquals(1, actionMap['EntryLinkClick']);
+    assertEquals(1, histogramMap['HistoryPage.ClickPosition']![1]);
+    assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset']![1]);
+
+    // TODO(https://crbug.com/1000573): Log the contents of this histogram
+    // for debugging in case the flakiness reoccurs.
+    console.log(Object.keys(histogramMap['HistoryPage.ClickAgeInDays']!));
+
+    // The "age in days" histogram should record 8 days, since the history
+    // entry was created between 7 and 8 days ago and we round the
+    // recorded value up.
+    assertEquals(1, histogramMap['HistoryPage.ClickAgeInDays']![8]);
+    assertEquals(1, histogramMap['HistoryPage.ClickAgeInDaysSubset']![8]);
+
+    testService.resetResolver('queryHistory');
+    testService.setQueryResult({
+      info: createHistoryInfo('goog'),
+      value: [
+        createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
+        createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
+        createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
+      ],
+    });
+    app.dispatchEvent(new CustomEvent(
+        'change-query',
+        {bubbles: true, composed: true, detail: {search: 'goog'}}));
+    assertEquals(1, actionMap['Search']);
+    app.set('queryState_.incremental', true);
+    await Promise.all([
+      testService.whenCalled('queryHistory'),
+      flushTasks(),
+    ]);
+
+    app.$.history.shadowRoot!.querySelector('iron-list')!.fire('iron-resize');
+    await waitAfterNextRender(app.$.history);
+    flush();
+
+    items = app.$.history.shadowRoot!.querySelectorAll('history-item');
+    assertTrue(!!items[0]);
+    assertTrue(!!items[4]);
+    items[0].$.link.click();
+    assertEquals(1, actionMap['SearchResultClick']);
+    assertEquals(1, histogramMap['HistoryPage.ClickPosition']![0]);
+    assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset']![0]);
+    items[0].$.checkbox.click();
+    items[4].$.checkbox.click();
+    await flushTasks();
+
+    app.$.toolbar.deleteSelectedItems();
+    assertEquals(1, actionMap['RemoveSelected']);
+    await flushTasks();
+
+    app.$.history.shadowRoot!.querySelector<HTMLElement>(
+                                 '.cancel-button')!.click();
+    assertEquals(1, actionMap['CancelRemoveSelected']);
+    app.$.toolbar.deleteSelectedItems();
+    await flushTasks();
+
+    app.$.history.shadowRoot!.querySelector<HTMLElement>(
+                                 '.action-button')!.click();
+    assertEquals(1, actionMap['ConfirmRemoveSelected']);
+    await flushTasks();
+
+    items = app.$.history.shadowRoot!.querySelectorAll('history-item');
+    assertTrue(!!items[0]);
+    items[0].$['menu-button'].click();
+    await flushTasks();
+
+    app.$.history.shadowRoot!.querySelector<HTMLElement>(
+                                 '#menuRemoveButton')!.click();
+    await Promise.all([
+      testService.whenCalled('removeVisits'),
+      flushTasks(),
+    ]);
+
+    assertEquals(1, histogramMap['HistoryPage.RemoveEntryPosition']![0]);
+    assertEquals(1, histogramMap['HistoryPage.RemoveEntryPositionSubset']![0]);
+  });
+
+  test('synced-device-manager', async () => {
+    const sessionList = [
+      createSession(
+          'Nexus 5',
+          [createWindow(['http://www.google.com', 'http://example.com'])]),
+      createSession(
+          'Nexus 6',
+          [
+            createWindow(['http://test.com']),
+            createWindow(['http://www.gmail.com', 'http://badssl.com'])
+          ]),
+    ];
+    testService.setForeignSessions(sessionList);
+    await finishSetup([]);
+
+    navigateTo('/syncedTabs');
+    await flushTasks();
+
+    const histogram = histogramMap[SYNCED_TABS_HISTOGRAM_NAME];
+    assertTrue(!!histogram);
+    assertEquals(1, histogram[SyncedTabsHistogram.INITIALIZED]);
+
+    await testService.whenCalled('getForeignSessions');
+    await flushTasks();
+
+    assertEquals(1, histogram[SyncedTabsHistogram.HAS_FOREIGN_DATA]);
+    await flushTasks();
+
+    const syncedDeviceManager =
+        app.shadowRoot!.querySelector('history-synced-device-manager');
+    assertTrue(!!syncedDeviceManager);
+
+    const cards = syncedDeviceManager.shadowRoot!.querySelectorAll(
+        'history-synced-device-card');
+    assertTrue(!!cards[0]);
+    cards[0].$['card-heading'].click();
+    assertEquals(1, histogram[SyncedTabsHistogram.COLLAPSE_SESSION]);
+    cards[0].$['card-heading'].click();
+    assertEquals(1, histogram[SyncedTabsHistogram.EXPAND_SESSION]);
+    cards[0].shadowRoot!.querySelectorAll<HTMLElement>(
+                            '.website-link')[0]!.click();
+    assertEquals(1, histogram[SyncedTabsHistogram.LINK_CLICKED]);
+
+    const menuButton = cards[0].$['menu-button'];
+    menuButton.click();
+    await flushTasks();
+
+    syncedDeviceManager.shadowRoot!
+        .querySelector<HTMLElement>('#menuOpenButton')!.click();
+    assertEquals(1, histogram[SyncedTabsHistogram.OPEN_ALL]);
+
+    menuButton.click();
+    await flushTasks();
+
+    syncedDeviceManager!.shadowRoot!
+        .querySelector<HTMLElement>('#menuDeleteButton')!.click();
+    assertEquals(1, histogram[SyncedTabsHistogram.HIDE_FOR_NOW]);
+  });
+});
diff --git a/chrome/test/data/webui/history/history_routing_test.js b/chrome/test/data/webui/history/history_routing_test.ts
similarity index 74%
rename from chrome/test/data/webui/history/history_routing_test.js
rename to chrome/test/data/webui/history/history_routing_test.ts
index a3fe0b2..c09abaa 100644
--- a/chrome/test/data/webui/history/history_routing_test.js
+++ b/chrome/test/data/webui/history/history_routing_test.ts
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxyImpl, BrowserServiceImpl, MetricsProxyImpl} from 'chrome://history/history.js';
+import 'chrome://history/history.js';
+
+import {BrowserProxyImpl, BrowserServiceImpl, HistoryAppElement, HistorySideBarElement, MetricsProxyImpl} from 'chrome://history/history.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserProxy, TestMetricsProxy} from './history_clusters/utils.js';
@@ -13,16 +16,17 @@
 [true, false].forEach(isHistoryClustersEnabled => {
   const suitSuffix = isHistoryClustersEnabled ? 'enabled' : 'disabled';
   suite(`routing-test-with-history-clusters-${suitSuffix}`, function() {
-    let app;
-    let list;
-    let sidebar;
-    let toolbar;
+    let app: HistoryAppElement;
+    let sidebar: HistorySideBarElement;
+    let testBrowserProxy: TestBrowserProxy;
+    let testMetricsProxy: TestMetricsProxy;
 
-    function navigateTo(route) {
+    function navigateTo(route: string) {
       window.history.replaceState({}, '', route);
       window.dispatchEvent(new CustomEvent('location-changed'));
       // Update from the URL synchronously.
-      app.$$('history-router').debouncer_.flush();
+      app.shadowRoot!.querySelector(
+                         'history-router')!.getDebouncerForTesting()!.flush();
     }
 
     suiteSetup(() => {
@@ -38,14 +42,15 @@
       window.history.replaceState({}, '', '/');
       document.body.innerHTML = '';
       BrowserServiceImpl.setInstance(new TestBrowserService());
-      BrowserProxyImpl.setInstance(new TestBrowserProxy());
-      MetricsProxyImpl.setInstance(new TestMetricsProxy());
+      testBrowserProxy = new TestBrowserProxy();
+      BrowserProxyImpl.setInstance(testBrowserProxy);
+      testMetricsProxy = new TestMetricsProxy();
+      MetricsProxyImpl.setInstance(testMetricsProxy);
       app = document.createElement('history-app');
       document.body.appendChild(app);
 
       assertEquals('chrome://history/', window.location.href);
       sidebar = app.$['content-side-bar'];
-      toolbar = app.$['toolbar'];
       return flushTasks();
     });
 
@@ -69,37 +74,44 @@
 
     test('changing route changes active view', function() {
       assertEquals('history', app.$.content.selected);
-      assertEquals(app.$$('#history'), app.$['tabs-content'].selectedItem);
+      assertEquals(app.$.history, app.$['tabs-content'].selectedItem);
 
       navigateTo('/syncedTabs');
       return flushTasks().then(function() {
         assertEquals('chrome://history/syncedTabs', window.location.href);
 
         assertEquals('syncedTabs', app.$.content.selected);
-        assertEquals(app.$$('#synced-devices'), app.$.content.selectedItem);
+        assertEquals(
+            app.shadowRoot!.querySelector('#synced-devices'),
+            app.$.content.selectedItem);
       });
     });
 
     test('routing to /journeys may change active view', function() {
       assertEquals('history', app.$.content.selected);
-      assertEquals(app.$$('#history'), app.$['tabs-content'].selectedItem);
+      assertEquals(
+          app.shadowRoot!.querySelector('#history'),
+          app.$['tabs-content'].selectedItem);
 
       navigateTo('/journeys');
       return flushTasks().then(function() {
         assertEquals('chrome://history/journeys', window.location.href);
 
         assertEquals('history', app.$.content.selected);
-        assertEquals(!!app.$$('#history-clusters'), isHistoryClustersEnabled);
         assertEquals(
-            isHistoryClustersEnabled ? app.$$('#history-clusters') :
-                                       app.$$('#history'),
+            !!app.shadowRoot!.querySelector('#history-clusters'),
+            isHistoryClustersEnabled);
+        assertEquals(
+            isHistoryClustersEnabled ?
+                app.shadowRoot!.querySelector('#history-clusters') :
+                app.shadowRoot!.querySelector('#history'),
             app.$['tabs-content'].selectedItem);
       });
     });
 
     test('routing to /journeys may update sidebar menu item', function() {
       assertEquals('chrome://history/', sidebar.$.history.href);
-      assertEquals('history', sidebar.$.history.path);
+      assertEquals('history', sidebar.$.history.getAttribute('path'));
 
       navigateTo('/journeys');
       return flushTasks().then(function() {
@@ -110,7 +122,7 @@
             sidebar.$.history.href);
         assertEquals(
             isHistoryClustersEnabled ? 'journeys' : 'history',
-            sidebar.$.history.path);
+            sidebar.$.history.getAttribute('path'));
       });
     });
 
@@ -127,10 +139,11 @@
       assertEquals('history', sidebar.$.menu.selected);
       assertEquals('chrome://history/', window.location.href);
 
-      const historyTabs = app.$$('cr-tabs');
+      const historyTabs = app.shadowRoot!.querySelector('cr-tabs');
       assertEquals(!!historyTabs, isHistoryClustersEnabled);
 
       if (isHistoryClustersEnabled) {
+        assertTrue(!!historyTabs);
         historyTabs.selected = 1;
         assertEquals('journeys', sidebar.$.menu.selected);
         assertEquals('chrome://history/journeys', window.location.href);
@@ -155,30 +168,30 @@
         return;
       }
 
-      const handler = BrowserProxyImpl.getInstance().handler;
+      const handler = testBrowserProxy.handler;
 
       assertEquals('history', sidebar.$.menu.selected);
       assertEquals('chrome://history/', window.location.href);
 
       // Navigate to chrome://history/journeys.
-      app.$$('cr-tabs').selected = 1;
+      app.shadowRoot!.querySelector('cr-tabs')!.selected = 1;
       assertEquals('journeys', sidebar.$.menu.selected);
       assertEquals('chrome://history/journeys', window.location.href);
       await flushTasks();
-      assertTrue(
-          app.$$('#history-clusters').classList.contains('iron-selected'));
+      assertTrue(app.shadowRoot!.querySelector('#history-clusters')!.classList
+                     .contains('iron-selected'));
 
       handler.setResultFor(
           'toggleVisibility', Promise.resolve({visible: false}));
 
       // Verify the menu item label and press it.
       assertEquals(
-          'Disable', sidebar.$['toggle-history-clusters'].textContent.trim());
+          'Disable', sidebar.$['toggle-history-clusters'].textContent!.trim());
       keyDownOn(sidebar.$['toggle-history-clusters'], 0, '', ' ');
 
       // Verify that the browser is notified and the histogram is recorded.
-      let visible = await MetricsProxyImpl.getInstance().whenCalled(
-          'recordToggledVisibility');
+      let visible =
+          await testMetricsProxy.whenCalled('recordToggledVisibility');
       assertFalse(visible);
       visible = await handler.whenCalled('toggleVisibility');
       assertFalse(visible);
@@ -186,23 +199,22 @@
       // Toggling history clusters off navigates to chrome://history/.
       assertEquals('history', sidebar.$.menu.selected);
       assertEquals('chrome://history/', window.location.href);
-      assertFalse(
-          app.$$('#history-clusters').classList.contains('iron-selected'));
+      assertFalse(app.shadowRoot!.querySelector('#history-clusters')!.classList
+                      .contains('iron-selected'));
 
       handler.reset();
-      MetricsProxyImpl.getInstance().reset();
+      testMetricsProxy.reset();
 
       handler.setResultFor(
           'toggleVisibility', Promise.resolve({visible: true}));
 
       // Verify the updated menu item label and press it again.
       assertEquals(
-          'Enable', sidebar.$['toggle-history-clusters'].textContent.trim());
+          'Enable', sidebar.$['toggle-history-clusters'].textContent!.trim());
       keyDownOn(sidebar.$['toggle-history-clusters'], 0, '', ' ');
 
       // Verify that the browser is notified and the histogram is recorded.
-      visible = await MetricsProxyImpl.getInstance().whenCalled(
-          'recordToggledVisibility');
+      visible = await testMetricsProxy.whenCalled('recordToggledVisibility');
       assertTrue(visible);
       visible = await handler.whenCalled('toggleVisibility');
       assertTrue(visible);
@@ -210,8 +222,8 @@
       // Toggling history clusters on navigates to chrome://history/journeys.
       assertEquals('journeys', sidebar.$.menu.selected);
       assertEquals('chrome://history/journeys', window.location.href);
-      assertTrue(
-          app.$$('#history-clusters').classList.contains('iron-selected'));
+      assertTrue(app.shadowRoot!.querySelector('#history-clusters')!.classList
+                     .contains('iron-selected'));
     });
 
     test('search updates from route', function() {
@@ -219,13 +231,15 @@
       const searchTerm = 'Mei';
       assertEquals('history', app.$.content.selected);
       navigateTo('/?q=' + searchTerm);
-      assertEquals(searchTerm, toolbar.searchTerm);
+      assertEquals(searchTerm, app.$.toolbar.searchTerm);
     });
 
     test('route updates from search', function() {
       const searchTerm = 'McCree';
       assertEquals('history', app.$.content.selected);
-      app.fire('change-query', {search: searchTerm});
+      app.dispatchEvent(new CustomEvent(
+          'change-query',
+          {bubbles: true, composed: true, detail: {search: searchTerm}}));
       assertEquals('chrome://history/?q=' + searchTerm, window.location.href);
     });
 
@@ -236,19 +250,19 @@
 
       sidebar.$.syncedTabs.click();
       assertEquals('syncedTabs', sidebar.$.menu.selected);
-      assertEquals(searchTerm, toolbar.searchTerm);
+      assertEquals(searchTerm, app.$.toolbar.searchTerm);
       assertEquals(
           'chrome://history/syncedTabs?q=' + searchTerm, window.location.href);
 
       sidebar.$.history.click();
       assertEquals('history', sidebar.$.menu.selected);
-      assertEquals(searchTerm, toolbar.searchTerm);
+      assertEquals(searchTerm, app.$.toolbar.searchTerm);
       assertEquals('chrome://history/?q=' + searchTerm, window.location.href);
 
       if (isHistoryClustersEnabled) {
-        app.$$('cr-tabs').selected = 1;
+        app.shadowRoot!.querySelector('cr-tabs')!.selected = 1;
         assertEquals('journeys', sidebar.$.menu.selected);
-        assertEquals(searchTerm, toolbar.searchTerm);
+        assertEquals(searchTerm, app.$.toolbar.searchTerm);
         assertEquals(
             'chrome://history/journeys?q=' + searchTerm, window.location.href);
       }
diff --git a/chrome/test/data/webui/history/history_routing_with_query_param_test.js b/chrome/test/data/webui/history/history_routing_with_query_param_test.ts
similarity index 78%
rename from chrome/test/data/webui/history/history_routing_with_query_param_test.js
rename to chrome/test/data/webui/history/history_routing_with_query_param_test.ts
index 625a52f..b60714a 100644
--- a/chrome/test/data/webui/history/history_routing_with_query_param_test.js
+++ b/chrome/test/data/webui/history/history_routing_with_query_param_test.ts
@@ -2,17 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl} from 'chrome://history/history.js';
+import 'chrome://history/history.js';
+
+import {BrowserServiceImpl, HistoryAppElement} from 'chrome://history/history.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryInfo} from './test_util.js';
 
 suite('routing-with-query-param', function() {
-  let app;
-  let toolbar;
-  let expectedQuery;
-  let testService;
+  let app: HistoryAppElement;
+  let expectedQuery: string;
+  let testService: TestBrowserService;
 
   setup(function() {
     document.body.innerHTML = '';
@@ -29,7 +31,6 @@
     });
     app = document.createElement('history-app');
     document.body.appendChild(app);
-    toolbar = app.$['toolbar'];
     expectedQuery = 'query';
   });
 
@@ -42,7 +43,7 @@
         .then(function() {
           assertEquals(
               expectedQuery,
-              toolbar.$['main-toolbar'].getSearchField().getValue());
+              app.$.toolbar.$.mainToolbar.getSearchField().getValue());
         });
   });
 });
diff --git a/chrome/test/data/webui/history/history_synced_device_manager_focus_test.js b/chrome/test/data/webui/history/history_synced_device_manager_focus_test.js
index 3fd5624..ac01f2d 100644
--- a/chrome/test/data/webui/history/history_synced_device_manager_focus_test.js
+++ b/chrome/test/data/webui/history/history_synced_device_manager_focus_test.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {ensureLazyLoaded} from 'chrome://history/history.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
@@ -33,14 +34,8 @@
 
     element.sessionList = sessionList;
 
-    let lastFocused;
     let cards;
     let focused;
-    const onFocusHandler = element.focusGrid_.onFocus;
-    element.focusGrid_.onFocus = function(row, e) {
-      onFocusHandler.call(element.focusGrid_, row, e);
-      lastFocused = e.currentTarget;
-    };
 
     await flushTasks();
     cards = polymerSelectAll(element, 'history-synced-device-card');
@@ -51,36 +46,36 @@
     // Go to the collapse button.
     pressAndReleaseKeyOn(focused, 39, [], 'ArrowRight');
     focused = cards[0].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     // Go to the first url.
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     focused = polymerSelectAll(cards[0], '.website-link')[0];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     // Collapse the first card.
     pressAndReleaseKeyOn(focused, 38, [], 'ArrowUp');
     focused = cards[0].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
     focused.click();
     await flushTasks();
 
     // Pressing down goes to the next card.
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     focused = cards[1].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     // Expand the first card.
     pressAndReleaseKeyOn(focused, 38, [], 'ArrowUp');
     focused = cards[0].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
     focused.click();
     await flushTasks();
 
     // First card's urls are focusable again.
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     focused = polymerSelectAll(cards[0], '.website-link')[0];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     // Remove the second URL from the first card.
     sessionList[0].windows[0].tabs.splice(1, 1);
@@ -92,11 +87,11 @@
     // Go to the next card's menu buttons.
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     focused = cards[1].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     pressAndReleaseKeyOn(focused, 38, [], 'ArrowUp');
     focused = polymerSelectAll(cards[0], '.website-link')[0];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
 
     // Remove the second card.
     sessionList.splice(1, 1);
@@ -108,6 +103,6 @@
     // Pressing down goes to the next card.
     pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
     focused = cards[1].$['collapse-button'];
-    assertEquals(focused, lastFocused);
+    assertEquals(focused, getDeepActiveElement());
   });
 });
diff --git a/chrome/test/data/webui/history/history_toolbar_focus_test.js b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
similarity index 66%
rename from chrome/test/data/webui/history/history_toolbar_focus_test.js
rename to chrome/test/data/webui/history/history_toolbar_focus_test.ts
index 83e25c0a..b227f08 100644
--- a/chrome/test/data/webui/history/history_toolbar_focus_test.js
+++ b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
@@ -2,16 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl} from 'chrome://history/history.js';
+import 'chrome://history/history.js';
+
+import {BrowserServiceImpl, HistoryAppElement} from 'chrome://history/history.js';
 import {isMac} from 'chrome://resources/js/cr.m.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 
 suite('<history-toolbar>', function() {
-  let app;
-  let toolbar;
+  let app: HistoryAppElement;
 
   setup(function() {
     document.body.innerHTML = '';
@@ -20,40 +22,37 @@
 
     app = document.createElement('history-app');
     document.body.appendChild(app);
-
-    toolbar = app.$['toolbar'];
   });
 
   test('search bar is focused on load in wide mode', async () => {
-    toolbar.$['main-toolbar'].narrow = false;
+    app.$.toolbar.$.mainToolbar.narrow = false;
 
     await flushTasks();
 
     // Ensure the search bar is focused on load.
-    assertTrue(
-        app.$.toolbar.$['main-toolbar'].getSearchField().isSearchFocused());
+    assertTrue(app.$.toolbar.$.mainToolbar.getSearchField().isSearchFocused());
   });
 
   test('search bar is not focused on load in narrow mode', async () => {
-    toolbar.$['main-toolbar'].narrow = true;
+    app.$.toolbar.$.mainToolbar.narrow = true;
 
     await flushTasks();
     // Ensure the search bar is focused on load.
-    assertFalse(toolbar.$['main-toolbar'].getSearchField().isSearchFocused());
+    assertFalse(app.$.toolbar.$.mainToolbar.getSearchField().isSearchFocused());
   });
 
   test('shortcuts to open search field', function() {
-    const field = toolbar.$['main-toolbar'].getSearchField();
+    const field = app.$.toolbar.$.mainToolbar.getSearchField();
     field.blur();
     assertFalse(field.showingSearch);
 
     const modifier = isMac ? 'meta' : 'ctrl';
     pressAndReleaseKeyOn(document.body, 70, modifier, 'f');
     assertTrue(field.showingSearch);
-    assertEquals(field.$.searchInput, field.root.activeElement);
+    assertEquals(field.$.searchInput, field.shadowRoot!.activeElement);
 
     pressAndReleaseKeyOn(field.$.searchInput, 27, '', 'Escape');
     assertFalse(field.showingSearch, 'Pressing escape closes field.');
-    assertNotEquals(field.$.searchInput, field.root.activeElement);
+    assertNotEquals(field.$.searchInput, field.shadowRoot!.activeElement);
   });
 });
diff --git a/chrome/test/data/webui/history/history_toolbar_test.js b/chrome/test/data/webui/history/history_toolbar_test.ts
similarity index 69%
rename from chrome/test/data/webui/history/history_toolbar_test.js
rename to chrome/test/data/webui/history/history_toolbar_test.ts
index 61d56694..1492edb 100644
--- a/chrome/test/data/webui/history/history_toolbar_test.js
+++ b/chrome/test/data/webui/history/history_toolbar_test.ts
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import 'chrome://history/history.js';
+
+import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry} from 'chrome://history/history.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryEntry, createHistoryInfo} from './test_util.js';
 
 suite('history-toolbar', function() {
-  let app;
-  let element;
-  let toolbar;
-  let testService;
-  const TEST_HISTORY_RESULTS =
+  let app: HistoryAppElement;
+  let testService: TestBrowserService;
+  const TEST_HISTORY_RESULTS: [HistoryEntry] =
       [createHistoryEntry('2016-03-15', 'https://google.com')];
 
   setup(function() {
@@ -24,8 +24,6 @@
 
     app = document.createElement('history-app');
     document.body.appendChild(app);
-    element = app.$.history;
-    toolbar = app.$.toolbar;
     return Promise
         .all([
           ensureLazyLoaded(),
@@ -37,38 +35,37 @@
   test('selecting checkbox causes toolbar to change', function() {
     testService.setQueryResult(
         {info: createHistoryInfo(), value: TEST_HISTORY_RESULTS});
-    element.dispatchEvent(new CustomEvent(
+    app.$.history.dispatchEvent(new CustomEvent(
         'query-history', {bubbles: true, composed: true, detail: true}));
     return testService.whenCalled('queryHistoryContinuation')
         .then(flushTasks)
         .then(function() {
-          const item = element.shadowRoot.querySelector('history-item');
+          const item = app.$.history.shadowRoot!.querySelector('history-item')!;
           item.$.checkbox.click();
 
+          const toolbar = app.$.toolbar;
+
           // Ensure that when an item is selected that the count held by the
           // toolbar increases.
           assertEquals(1, toolbar.count);
-          // Ensure that the toolbar boolean states that at least one item is
-          // selected.
-          assertTrue(toolbar.itemsSelected_);
+          assertTrue(toolbar.$.mainToolbar.hasAttribute('has-overlay'));
 
           item.$.checkbox.click();
 
           // Ensure that when an item is deselected the count held by the
           // toolbar decreases.
           assertEquals(0, toolbar.count);
-          // Ensure that the toolbar boolean states that no items are selected.
-          assertFalse(toolbar.itemsSelected_);
+          assertFalse(toolbar.$.mainToolbar.hasAttribute('has-overlay'));
         });
   });
 
   test('search term gathered correctly from toolbar', function() {
     testService.resetResolver('queryHistory');
+    const toolbar = app.$.toolbar;
     testService.setQueryResult(
         {info: createHistoryInfo('Test'), value: TEST_HISTORY_RESULTS});
-    toolbar.shadowRoot.querySelector('cr-toolbar')
-        .dispatchEvent(new CustomEvent(
-            'search-changed', {bubbles: true, composed: true, detail: 'Test'}));
+    toolbar.$.mainToolbar.dispatchEvent(new CustomEvent(
+        'search-changed', {bubbles: true, composed: true, detail: 'Test'}));
     return testService.whenCalled('queryHistory').then(query => {
       assertEquals('Test', query);
     });
@@ -81,10 +78,9 @@
       info: createHistoryInfo('Test2'),
       value: TEST_HISTORY_RESULTS,
     });
-    toolbar.shadowRoot.querySelector('cr-toolbar')
-        .dispatchEvent(new CustomEvent(
-            'search-changed',
-            {bubbles: true, composed: true, detail: 'Test2'}));
+    const toolbar = app.$.toolbar;
+    toolbar.$.mainToolbar.dispatchEvent(new CustomEvent(
+        'search-changed', {bubbles: true, composed: true, detail: 'Test2'}));
     return testService.whenCalled('queryHistory')
         .then(flushTasks)
         .then(() => {
diff --git a/chrome/test/data/webui/history/link_click_test.js b/chrome/test/data/webui/history/link_click_test.ts
similarity index 94%
rename from chrome/test/data/webui/history/link_click_test.js
rename to chrome/test/data/webui/history/link_click_test.ts
index 604dea3..9f8536571 100644
--- a/chrome/test/data/webui/history/link_click_test.js
+++ b/chrome/test/data/webui/history/link_click_test.ts
@@ -4,6 +4,8 @@
 
 import {BrowserServiceImpl, listenForPrivilegedLinkClicks} from 'chrome://history/history.js';
 import {$} from 'chrome://resources/js/util.m.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+
 import {TestBrowserService} from './test_browser_service.js';
 
 suite('listenForPrivilegedLinkClicks unit test', function() {
diff --git a/chrome/test/data/webui/history/searched_label_test.js b/chrome/test/data/webui/history/searched_label_test.ts
similarity index 81%
rename from chrome/test/data/webui/history/searched_label_test.js
rename to chrome/test/data/webui/history/searched_label_test.ts
index cde83561..935914f 100644
--- a/chrome/test/data/webui/history/searched_label_test.js
+++ b/chrome/test/data/webui/history/searched_label_test.ts
@@ -3,11 +3,13 @@
 // found in the LICENSE file.
 
 import 'chrome://history/history.js';
+
+import {HistorySearchedLabelElement} from 'chrome://history/history.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('<history-searched-label> unit test', function() {
-  /** @type {?HistorySearchedLabelElement} */
-  let label;
+  let label: HistorySearchedLabelElement;
 
   setup(function() {
     document.body.innerHTML = '';
@@ -26,7 +28,7 @@
     flush();
     const boldItems = document.querySelectorAll('b');
     assertEquals(1, boldItems.length);
-    assertEquals(label.searchTerm, boldItems[0].textContent);
+    assertEquals(label.searchTerm, boldItems[0]!.textContent);
 
     label.searchTerm = 'g';
     flush();
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn
new file mode 100644
index 0000000..ecce2c78
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/BUILD.gn
@@ -0,0 +1,55 @@
+# Copyright 2022 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("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+import("./modules/modules.gni")
+import("./realbox/realbox.gni")
+
+# Test files that do not require preprocessing.
+non_preprocessed_files = [
+                           "app_test.js",
+                           "background_manager_test.js",
+                           "customize_backgrounds_test.js",
+                           "customize_dialog_focus_test.js",
+                           "customize_dialog_test.js",
+                           "customize_modules_test.js",
+                           "customize_shortcuts_test.js",
+                           "doodle_share_dialog_focus_test.js",
+                           "doodle_share_dialog_test.js",
+                           "logo_test.js",
+                           "metrics_test_support.js",
+                           "metrics_utils_test.js",
+                           "middle_slot_promo_test.js",
+                           "test_support.js",
+                           "utils_test.js",
+                           "voice_search_overlay_test.js",
+                         ] + modules_test_files + realbox_test_files
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_new_tab_page"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  resource_path_prefix = "new_tab_page"
+}
+
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [
+    "chrome://new_tab_page/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/new_tab_page/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+  in_files = non_preprocessed_files
+
+  deps = [ "//chrome/browser/resources/new_tab_page:build_ts" ]
+  extra_deps = [ "..:generate_definitions" ]
+}
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js
index 6840766..1baa77f 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.js
+++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, BackgroundManager, BrowserCommandProxy, CustomizeDialogPage, ModuleRegistry, NewTabPageProxy, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
@@ -10,10 +10,11 @@
 import {isMac} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {assertNotStyle, assertStyle, createTheme, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js';
+import {assertNotStyle, assertStyle, createTheme, installMock} from './test_support.js';
 
 suite('NewTabPageAppTest', () => {
   /** @type {!AppElement} */
diff --git a/chrome/test/data/webui/new_tab_page/background_manager_test.js b/chrome/test/data/webui/new_tab_page/background_manager_test.js
index 5f32b0ba..27895eb 100644
--- a/chrome/test/data/webui/new_tab_page/background_manager_test.js
+++ b/chrome/test/data/webui/new_tab_page/background_manager_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {BackgroundManager} from 'chrome://new-tab-page/new_tab_page.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.js b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.js
index 13ac400..3728e5a 100644
--- a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
-import {assertNotStyle, assertStyle, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+
+import {assertNotStyle, assertStyle, installMock} from './test_support.js';
 
 function createCollection(id = 0, label = '', url = '') {
   return {id: id, label: label, previewImageUrl: {url: url}};
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
index f95b832f..14825b1 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {keydown} from 'chrome://test/new_tab_page/test_support.js';
-import {flushTasks} from 'chrome://test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {keydown} from './test_support.js';
 
 suite('NewTabPageCustomizeDialogFocusTest', () => {
   /** @type {!CustomizeDialogElement} */
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_test.js b/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
index df9817cf..1d7ef3e5 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {CustomizeDialogPage, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from './test_support.js';
 
 suite('NewTabPageCustomizeDialogTest', () => {
   /** @type {!CustomizeDialogElement} */
diff --git a/chrome/test/data/webui/new_tab_page/customize_modules_test.js b/chrome/test/data/webui/new_tab_page/customize_modules_test.js
index 0caeb11..5c97dca 100644
--- a/chrome/test/data/webui/new_tab_page/customize_modules_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_modules_test.js
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, ChromeCartProxy, ModuleDescriptor, ModuleRegistry, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {assertNotStyle, assertStyle, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {isVisible} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js';
+import {assertNotStyle, assertStyle, installMock} from './test_support.js';
 
 /**
  * @param {!HTMLElement} host
diff --git a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
index ad08603..f0036c2 100644
--- a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+import {installMock} from './test_support.js';
 
 suite('NewTabPageCustomizeShortcutsTest', () => {
   /** @type {!CustomizeShortcutsElement} */
diff --git a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.js b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.js
index 51f755b..e313df8d4 100644
--- a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.js
+++ b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/new_tab_page.js';
 
 suite('NewTabPageDoodleShareDialogFocusTest', () => {
diff --git a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.js b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.js
index d8dcf3a..87bcc04d 100644
--- a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.js
+++ b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+import {installMock} from './test_support.js';
 
 suite('NewTabPageDoodleShareDialogTest', () => {
   /** @type {!DoodleShareDialogElement} */
diff --git a/chrome/test/data/webui/new_tab_page/logo_test.js b/chrome/test/data/webui/new_tab_page/logo_test.js
index 49a62124a..4a484617 100644
--- a/chrome/test/data/webui/new_tab_page/logo_test.js
+++ b/chrome/test/data/webui/new_tab_page/logo_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {Doodle, DoodleImageType, DoodleShareChannel, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
-import {assertNotStyle, assertStyle, installMock, keydown} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {assertNotStyle, assertStyle, installMock, keydown} from './test_support.js';
 
 /**
  * @param {!Element} element
diff --git a/chrome/test/data/webui/new_tab_page/metrics_utils_test.js b/chrome/test/data/webui/new_tab_page/metrics_utils_test.js
index 5b319c6d..f3ea2e7 100644
--- a/chrome/test/data/webui/new_tab_page/metrics_utils_test.js
+++ b/chrome/test/data/webui/new_tab_page/metrics_utils_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {recordDuration, recordLoadDuration, recordOccurence, recordPerdecage} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
diff --git a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
index 4a96798..bb6716a 100644
--- a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
+++ b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {$$, BrowserCommandProxy, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command/browser_command.mojom-webui.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from './test_support.js';
 
 suite('NewTabPageMiddleSlotPromoTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/cart.gni b/chrome/test/data/webui/new_tab_page/modules/cart/cart.gni
new file mode 100644
index 0000000..9e1c9d7
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/cart.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+cart_test_files = [ "modules/cart/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
index 31194366..1167fb40 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, chromeCartDescriptor, ChromeCartProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {assertNotStyle, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
+import {assertNotStyle, installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesChromeCartModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/cart_v2.gni b/chrome/test/data/webui/new_tab_page/modules/cart_v2/cart_v2.gni
new file mode 100644
index 0000000..3815a1e
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/cart_v2.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+cart_v2_test_files = [ "modules/cart_v2/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
index 1ea58ea..1330c99 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, ChromeCartProxy, chromeCartV2Descriptor, ModuleHeight} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {assertNotStyle, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
+import {assertNotStyle, installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesChromeCartModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/drive.gni b/chrome/test/data/webui/new_tab_page/modules/drive/drive.gni
new file mode 100644
index 0000000..0efac6d
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/drive.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+drive_test_files = [ "modules/drive/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
index 22a6bc3..7474f3f8 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
 import {$$, driveDescriptor, DriveProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesDriveModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive_v2/drive_v2.gni b/chrome/test/data/webui/new_tab_page/modules/drive_v2/drive_v2.gni
new file mode 100644
index 0000000..0eca56d
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/drive_v2/drive_v2.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+drive_v2_test_files = [ "modules/drive_v2/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
index c77eff2..fd42401 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
 import {$$, DriveProxy, driveV2Descriptor} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertEquals, assertTrue} from 'chrome://test/chai_assert.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesDriveModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/dummy_v2/dummy_v2.gni b/chrome/test/data/webui/new_tab_page/modules/dummy_v2/dummy_v2.gni
new file mode 100644
index 0000000..61f70ef
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/dummy_v2/dummy_v2.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+dummy_v2_test_files = [ "modules/dummy_v2/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/dummy_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/dummy_v2/module_test.js
index 964f8be..12636f8 100644
--- a/chrome/test/data/webui/new_tab_page/modules/dummy_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/dummy_v2/module_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {FooHandlerRemote} from 'chrome://new-tab-page/foo.mojom-webui.js';
 import {$$, dummyV2Descriptor, FooProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesDummyModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/info_dialog_test.js b/chrome/test/data/webui/new_tab_page/modules/info_dialog_test.js
index 194eb4e5..0572a37 100644
--- a/chrome/test/data/webui/new_tab_page/modules/info_dialog_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/info_dialog_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, InfoDialogElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js
index c893e316..cb5d715 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {ModuleDescriptor, ModuleDescriptorV2, ModuleHeight, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertEquals, assertTrue} from 'chrome://test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {createElement, initNullModule, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from '../metrics_test_support.js';
+import {createElement, initNullModule, installMock} from '../test_support.js';
 
 suite('NewTabPageModulesModuleDescriptorTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_header_test.js b/chrome/test/data/webui/new_tab_page/modules/module_header_test.js
index d5eb2e7..d273458 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_header_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/module_header_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, ModuleHeaderElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js
index 7c6cb61..e5a8a8a4 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {ModuleDescriptor, ModuleRegistry, NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {assertDeepEquals, assertEquals} from '../../chai_assert.js';
 import {TestBrowserProxy} from '../../test_browser_proxy.js';
-import {flushTasks} from '../../test_util.js';
 import {fakeMetricsPrivate, MetricsTracker} from '../metrics_test_support.js';
 import {createElement, initNullModule, installMock} from '../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js
index 552d25d0..a367055 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, ModuleDescriptor, ModuleDescriptorV2, ModuleHeight, ModuleWrapperElement, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertDeepEquals, assertEquals, assertThrows} from 'chrome://test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {createElement, initNullModule, installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise} from 'chrome://test/test_util.js';
+import {assertDeepEquals, assertEquals, assertThrows} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from '../metrics_test_support.js';
+import {createElement, initNullModule, installMock} from '../test_support.js';
 
 suite('NewTabPageModulesModuleWrapperTest', () => {
   /** @type {!ModuleWrapperElement} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules.gni b/chrome/test/data/webui/new_tab_page/modules/modules.gni
new file mode 100644
index 0000000..9be422d
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/modules.gni
@@ -0,0 +1,31 @@
+# Copyright 2022 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("./cart/cart.gni")
+import("./cart_v2/cart_v2.gni")
+import("./drive/drive.gni")
+import("./drive_v2/drive_v2.gni")
+import("./photos/photos.gni")
+import("./recipes_v2/recipes_v2.gni")
+import("./task_module/task_module.gni")
+
+if (!is_official_build) {
+  import("./dummy_v2/dummy_v2.gni")
+}
+
+modules_test_files =
+    [
+      "modules/info_dialog_test.js",
+      "modules/module_descriptor_test.js",
+      "modules/module_header_test.js",
+      "modules/module_registry_test.js",
+      "modules/module_wrapper_test.js",
+      "modules/modules_test.js",
+    ] + cart_test_files + cart_v2_test_files + drive_test_files +
+    drive_v2_test_files + photos_test_files + recipes_v2_test_files +
+    task_module_test_files
+
+if (!is_official_build) {
+  modules_test_files += dummy_v2_test_files
+}
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.js b/chrome/test/data/webui/new_tab_page/modules/modules_test.js
index 9d3bf4957..dfc6d80b 100644
--- a/chrome/test/data/webui/new_tab_page/modules/modules_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, ModuleDescriptor, ModuleDescriptorV2, ModuleHeight, ModuleRegistry, ModulesElement, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
index 722d1e2..02d1084 100644
--- a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, photosDescriptor, PhotosProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PhotosHandlerRemote} from 'chrome://new-tab-page/photos.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {isVisible} from 'chrome://test/test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesPhotosModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/photos.gni b/chrome/test/data/webui/new_tab_page/modules/photos/photos.gni
new file mode 100644
index 0000000..61b68c1
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/photos/photos.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+photos_test_files = [ "modules/photos/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/recipes_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/recipes_v2/module_test.js
index 75af7b12..8464758 100644
--- a/chrome/test/data/webui/new_tab_page/modules/recipes_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/recipes_v2/module_test.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, recipeTasksV2Descriptor, TaskModuleHandlerProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {TaskModuleHandlerRemote} from 'chrome://new-tab-page/task_module.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertEquals, assertTrue} from 'chrome://test/chai_assert.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesRecipesV2ModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/recipes_v2/recipes_v2.gni b/chrome/test/data/webui/new_tab_page/modules/recipes_v2/recipes_v2.gni
new file mode 100644
index 0000000..42239e2
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/recipes_v2/recipes_v2.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+recipes_v2_test_files = [ "modules/recipes_v2/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/modules/task_module/module_test.js b/chrome/test/data/webui/new_tab_page/modules/task_module/module_test.js
index 5311113..83f16952 100644
--- a/chrome/test/data/webui/new_tab_page/modules/task_module/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/task_module/module_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, shoppingTasksDescriptor, TaskModuleHandlerProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {TaskModuleHandlerRemote, TaskModuleType} from 'chrome://new-tab-page/task_module.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://test/chai_assert.js';
-import {installMock} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
+import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {installMock} from '../../test_support.js';
 
 suite('NewTabPageModulesTaskModuleTest', () => {
   /** @type {!TestBrowserProxy} */
diff --git a/chrome/test/data/webui/new_tab_page/modules/task_module/task_module.gni b/chrome/test/data/webui/new_tab_page/modules/task_module/task_module.gni
new file mode 100644
index 0000000..0a1af35
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/modules/task_module/task_module.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+task_module_test_files = [ "modules/task_module/module_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
index 36fd45e..23cf730 100644
--- a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
+++ b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
@@ -22,7 +22,7 @@
 var NewTabPageAppTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/app_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/app_test.js&host=webui-test';
   }
 };
 
@@ -57,7 +57,7 @@
 var NewTabPageCustomizeDialogTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_dialog_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_dialog_test.js&host=webui-test';
   }
 };
 
@@ -68,7 +68,7 @@
 var NewTabPageUtilsTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/utils_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/utils_test.js&host=webui-test';
   }
 };
 
@@ -79,7 +79,7 @@
 var NewTabPageMetricsUtilsTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/metrics_utils_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/metrics_utils_test.js&host=webui-test';
   }
 };
 
@@ -90,7 +90,7 @@
 var NewTabPageCustomizeShortcutsTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_shortcuts_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_shortcuts_test.js&host=webui-test';
   }
 };
 
@@ -101,7 +101,7 @@
 var NewTabPageCustomizeModulesTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_modules_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_modules_test.js&host=webui-test';
   }
 };
 
@@ -112,7 +112,7 @@
 var NewTabPageCustomizeBackgroundsTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_backgrounds_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_backgrounds_test.js&host=webui-test';
   }
 };
 
@@ -123,7 +123,7 @@
 var NewTabPageVoiceSearchOverlayTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/voice_search_overlay_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/voice_search_overlay_test.js&host=webui-test';
   }
 };
 
@@ -134,7 +134,7 @@
 var NewTabPageRealboxTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/realbox/realbox_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/realbox/realbox_test.js&host=webui-test';
   }
 };
 
@@ -145,7 +145,7 @@
 var NewTabPageLogoTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/logo_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/logo_test.js&host=webui-test';
   }
 };
 
@@ -156,7 +156,7 @@
 var NewTabPageDoodleShareDialogTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/doodle_share_dialog_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/doodle_share_dialog_test.js&host=webui-test';
   }
 };
 
@@ -167,7 +167,7 @@
 var NewTabPageBackgroundManagerTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/background_manager_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/background_manager_test.js&host=webui-test';
   }
 };
 
@@ -178,7 +178,7 @@
 var NewTabPageModulesModuleWrapperTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_wrapper_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_wrapper_test.js&host=webui-test';
   }
 };
 
@@ -189,7 +189,7 @@
 var NewTabPageModulesModulesTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/modules_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/modules_test.js&host=webui-test';
   }
 };
 
@@ -201,7 +201,7 @@
     class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_descriptor_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_descriptor_test.js&host=webui-test';
   }
 };
 
@@ -212,7 +212,7 @@
 var NewTabPageModulesModuleRegistryTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_registry_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_registry_test.js&host=webui-test';
   }
 };
 
@@ -223,7 +223,7 @@
 var NewTabPageModulesModuleHeaderTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_header_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/module_header_test.js&host=webui-test';
   }
 };
 
@@ -234,7 +234,7 @@
 var NewTabPageModulesInfoDialogTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/info_dialog_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/info_dialog_test.js&host=webui-test';
   }
 };
 
@@ -248,7 +248,7 @@
 var NewTabPageModulesDummyModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/dummy_v2/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/dummy_v2/module_test.js&host=webui-test';
   }
 };
 
@@ -261,7 +261,7 @@
 var NewTabPageMiddleSlotPromoTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/middle_slot_promo_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/middle_slot_promo_test.js&host=webui-test';
   }
 };
 
@@ -272,7 +272,7 @@
 var NewTabPageModulesDriveModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/drive/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/drive/module_test.js&host=webui-test';
   }
 };
 
@@ -283,7 +283,7 @@
 var NewTabPageModulesDriveV2ModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/drive_v2/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/drive_v2/module_test.js&host=webui-test';
   }
 };
 
@@ -294,7 +294,7 @@
 var NewTabPageModulesTaskModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/task_module/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/task_module/module_test.js&host=webui-test';
   }
 };
 
@@ -305,7 +305,7 @@
 var NewTabPageModulesRecipesV2ModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/recipes_v2/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/recipes_v2/module_test.js&host=webui-test';
   }
 };
 
@@ -317,7 +317,7 @@
     class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/cart/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/cart/module_test.js&host=webui-test';
   }
 };
 
@@ -330,7 +330,7 @@
     class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/cart_v2/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/cart_v2/module_test.js&host=webui-test';
   }
 };
 
@@ -339,7 +339,7 @@
 var NewTabPageModulesPhotosModuleTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/photos/module_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/modules/photos/module_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js b/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
index ebe2323..1bc7902 100644
--- a/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
+++ b/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
@@ -21,7 +21,7 @@
     class extends NewTabPageInteractiveTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_dialog_focus_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_dialog_focus_test.js&host=webui-test';
   }
 };
 
@@ -33,7 +33,7 @@
     class extends NewTabPageInteractiveTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/doodle_share_dialog_focus_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/doodle_share_dialog_focus_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox.gni b/chrome/test/data/webui/new_tab_page/realbox/realbox.gni
new file mode 100644
index 0000000..2355a8c
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox.gni
@@ -0,0 +1,5 @@
+# Copyright 2022 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.
+
+realbox_test_files = [ "realbox/realbox_test.js" ]
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.js b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.js
index 70729bd..1a124d59 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.js
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {$$, decodeString16, mojoString16, RealboxBrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/realbox.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-import {assertStyle, createTheme} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {eventToPromise} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {assertStyle, createTheme} from '../test_support.js';
 
 /**
  * @enum {string}
diff --git a/chrome/test/data/webui/new_tab_page/tsconfig_base.json b/chrome/test/data/webui/new_tab_page/tsconfig_base.json
new file mode 100644
index 0000000..eeddfb3
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "typeRoots": [
+       "./../../../../../third_party/node/node_modules/@types"
+    ]
+  }
+}
diff --git a/chrome/test/data/webui/new_tab_page/utils_test.js b/chrome/test/data/webui/new_tab_page/utils_test.js
index 5483040..2adede42 100644
--- a/chrome/test/data/webui/new_tab_page/utils_test.js
+++ b/chrome/test/data/webui/new_tab_page/utils_test.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {createScrollBorders, decodeString16, mojoString16} from 'chrome://new-tab-page/new_tab_page.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://test/test_util.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 suite('scroll borders', () => {
   /** @type {!HTMLElement} */
diff --git a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.js b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.js
index 3c44c35..2b14593 100644
--- a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.js
+++ b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.js
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://test/mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {$$, NewTabPageProxy, VoiceAction as Action, VoiceError as Error, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-import {flushTasks, isVisible} from 'chrome://test/test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertEquals} from '../chai_assert.js';
 
+import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js';
 import {assertNotStyle, assertStyle, installMock, keydown} from './test_support.js';
 
 function createResults(n) {
diff --git a/chrome/updater/mac/setup/setup.mm b/chrome/updater/mac/setup/setup.mm
index ca544e3..b5a3f8a9 100644
--- a/chrome/updater/mac/setup/setup.mm
+++ b/chrome/updater/mac/setup/setup.mm
@@ -337,6 +337,12 @@
 
 bool RemoveQuarantineAttributes(const base::FilePath& updater_bundle_path,
                                 const base::FilePath& updater_executable_path) {
+  if (!base::PathExists(updater_bundle_path)) {
+    VPLOG(1) << "Updater bundle path not found: "
+             << updater_bundle_path.value();
+    return false;
+  }
+
   if (!base::mac::RemoveQuarantineAttribute(updater_bundle_path)) {
     VPLOG(1) << "Could not remove com.apple.quarantine for the bundle.";
     return false;
diff --git a/chromecast/base/pref_names.cc b/chromecast/base/pref_names.cc
index 8b50c72..ab657ff9 100644
--- a/chromecast/base/pref_names.cc
+++ b/chromecast/base/pref_names.cc
@@ -20,12 +20,6 @@
 // Whether or not to report metrics and crashes.
 const char kOptInStats[] = "opt-in.stats";
 
-// Total number of child process crashes (other than renderer / extension
-// renderer ones, and plugin children, which are counted separately) since the
-// last report.
-const char kStabilityChildProcessCrashCount[] =
-    "user_experience_metrics.stability.child_process_crash_count";
-
 // Total number of kernel crashes since the last report.
 const char kStabilityKernelCrashCount[] =
     "user_experience_metrics.stability.kernel_crash_count";
diff --git a/chromecast/base/pref_names.h b/chromecast/base/pref_names.h
index c573933..539aa05 100644
--- a/chromecast/base/pref_names.h
+++ b/chromecast/base/pref_names.h
@@ -12,7 +12,6 @@
 extern const char kLatestDCSFeatures[];
 extern const char kMetricsIsNewClientID[];
 extern const char kOptInStats[];
-extern const char kStabilityChildProcessCrashCount[];
 extern const char kStabilityKernelCrashCount[];
 extern const char kStabilityOtherUserCrashCount[];
 extern const char kStabilityRendererCrashCount[];
diff --git a/chromecast/browser/metrics/cast_stability_metrics_provider.cc b/chromecast/browser/metrics/cast_stability_metrics_provider.cc
index 62e141d..e3dc6b8 100644
--- a/chromecast/browser/metrics/cast_stability_metrics_provider.cc
+++ b/chromecast/browser/metrics/cast_stability_metrics_provider.cc
@@ -50,7 +50,6 @@
 void CastStabilityMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
   registry->RegisterIntegerPref(prefs::kStabilityRendererFailedLaunchCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
 }
 
 CastStabilityMetricsProvider::CastStabilityMetricsProvider(
@@ -58,11 +57,6 @@
     PrefService* pref_service)
     : metrics_service_(metrics_service), pref_service_(pref_service) {
   DCHECK(pref_service_);
-  BrowserChildProcessObserver::Add(this);
-}
-
-CastStabilityMetricsProvider::~CastStabilityMetricsProvider() {
-  BrowserChildProcessObserver::Remove(this);
 }
 
 void CastStabilityMetricsProvider::OnRecordingEnabled() {
@@ -83,14 +77,7 @@
   ::metrics::SystemProfileProto_Stability* stability_proto =
       system_profile_proto->mutable_stability();
 
-  int count =
-      pref_service_->GetInteger(prefs::kStabilityChildProcessCrashCount);
-  if (count) {
-    stability_proto->set_child_process_crash_count(count);
-    pref_service_->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
-  }
-
-  count = pref_service_->GetInteger(prefs::kStabilityRendererCrashCount);
+  int count = pref_service_->GetInteger(prefs::kStabilityRendererCrashCount);
   if (count) {
     stability_proto->set_renderer_crash_count(count);
     pref_service_->SetInteger(prefs::kStabilityRendererCrashCount, 0);
@@ -144,14 +131,6 @@
   }
 }
 
-void CastStabilityMetricsProvider::BrowserChildProcessCrashed(
-    const content::ChildProcessData& data,
-    const content::ChildProcessTerminationInfo& info) {
-  IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
-  ::metrics::StabilityMetricsHelper::RecordStabilityEvent(
-      ::metrics::StabilityEventType::kChildProcessCrash);
-}
-
 void CastStabilityMetricsProvider::LogRendererCrash(
     content::RenderProcessHost* host,
     base::TerminationStatus status,
diff --git a/chromecast/browser/metrics/cast_stability_metrics_provider.h b/chromecast/browser/metrics/cast_stability_metrics_provider.h
index 1d7aa81..3bb135f 100644
--- a/chromecast/browser/metrics/cast_stability_metrics_provider.h
+++ b/chromecast/browser/metrics/cast_stability_metrics_provider.h
@@ -7,7 +7,6 @@
 
 #include "base/process/kill.h"
 #include "components/metrics/metrics_provider.h"
-#include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 
@@ -25,12 +24,10 @@
 namespace chromecast {
 namespace metrics {
 
-// CastStabilityMetricsProvider gathers and logs stability-related metrics.
-// Loosely based on ChromeStabilityMetricsProvider from chrome/browser/metrics.
-class CastStabilityMetricsProvider
-    : public ::metrics::MetricsProvider,
-      public content::BrowserChildProcessObserver,
-      public content::NotificationObserver {
+// Responsible for gathering and logging stability-related metrics. Loosely
+// based on the ContentStabilityMetricsProvider in components/metrics/content.
+class CastStabilityMetricsProvider : public ::metrics::MetricsProvider,
+                                     public content::NotificationObserver {
  public:
   // Registers local state prefs used by this class.
   static void RegisterPrefs(PrefRegistrySimple* registry);
@@ -42,7 +39,7 @@
   CastStabilityMetricsProvider& operator=(const CastStabilityMetricsProvider&) =
       delete;
 
-  ~CastStabilityMetricsProvider() override;
+  ~CastStabilityMetricsProvider() override = default;
 
   // metrics::MetricsDataProvider implementation:
   void OnRecordingEnabled() override;
@@ -59,11 +56,6 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // content::BrowserChildProcessObserver implementation:
-  void BrowserChildProcessCrashed(
-      const content::ChildProcessData& data,
-      const content::ChildProcessTerminationInfo& info) override;
-
   // Records a renderer process crash.
   void LogRendererCrash(content::RenderProcessHost* host,
                         base::TerminationStatus status,
diff --git a/chromeos/dbus/missive/missive_client.cc b/chromeos/dbus/missive/missive_client.cc
index f67b26d..6a69b80 100644
--- a/chromeos/dbus/missive/missive_client.cc
+++ b/chromeos/dbus/missive/missive_client.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -48,6 +49,7 @@
   }
 
   void Init(dbus::Bus* const bus) {
+    DCHECK(bus);
     origin_task_runner_ = bus->GetOriginTaskRunner();
 
     DCHECK(!missive_service_proxy_);
@@ -112,7 +114,7 @@
     ~DBusDelegate() override = default;
 
     // Writes request into dBus message writer.
-    virtual void WriteRequest(dbus::MessageWriter* writer) = 0;
+    virtual bool WriteRequest(dbus::MessageWriter* writer) = 0;
 
     // Parses response, retrieves status information from it and returns it.
     // Optional - returns OK if absent.
@@ -136,7 +138,14 @@
       dbus::MethodCall method_call(missive::kMissiveServiceInterface,
                                    dbus_method_);
       dbus::MessageWriter writer(&method_call);
-      WriteRequest(&writer);
+      if (!WriteRequest(&writer)) {
+        reporting::Status status(
+            reporting::error::UNKNOWN,
+            "MessageWriter was unable to append the request.");
+        LOG(ERROR) << status;
+        std::move(completion_callback_).Run(status);
+        return;
+      }
 
       // Make a dBus call.
       owner_->missive_service_proxy_->CallMethod(
@@ -166,6 +175,7 @@
         dbus::MessageReader reader(response_);
         status = ParseResponse(&reader);
       }
+      DCHECK(completion_callback_);
       std::move(completion_callback_).Run(status);
     }
 
@@ -192,13 +202,16 @@
       request_.set_priority(priority);
     }
 
-    void WriteRequest(dbus::MessageWriter* writer) override {
-      writer->AppendProtoAsArrayOfBytes(request_);
+    bool WriteRequest(dbus::MessageWriter* writer) override {
+      return writer->AppendProtoAsArrayOfBytes(request_);
     }
 
     reporting::Status ParseResponse(dbus::MessageReader* reader) override {
       reporting::EnqueueRecordResponse response_body;
-      reader->PopArrayOfBytesAsProto(&response_body);
+      if (!reader->PopArrayOfBytesAsProto(&response_body)) {
+        return reporting::Status(reporting::error::INTERNAL,
+                                 "Response was not parsable.");
+      }
       reporting::Status status;
       status.RestoreFrom(response_body.status());
       return status;
@@ -220,13 +233,16 @@
       request_.set_priority(priority);
     }
 
-    void WriteRequest(dbus::MessageWriter* writer) override {
-      writer->AppendProtoAsArrayOfBytes(request_);
+    bool WriteRequest(dbus::MessageWriter* writer) override {
+      return writer->AppendProtoAsArrayOfBytes(request_);
     }
 
     reporting::Status ParseResponse(dbus::MessageReader* reader) override {
       reporting::FlushPriorityResponse response_body;
-      reader->PopArrayOfBytesAsProto(&response_body);
+      if (!reader->PopArrayOfBytesAsProto(&response_body)) {
+        return reporting::Status(reporting::error::INTERNAL,
+                                 "Response was not parsable.");
+      }
       reporting::Status status;
       status.RestoreFrom(response_body.status());
       return status;
@@ -247,8 +263,8 @@
       *request_.mutable_signed_encryption_info() = encryption_info;
     }
 
-    void WriteRequest(dbus::MessageWriter* writer) override {
-      writer->AppendProtoAsArrayOfBytes(request_);
+    bool WriteRequest(dbus::MessageWriter* writer) override {
+      return writer->AppendProtoAsArrayOfBytes(request_);
     }
 
    private:
@@ -264,13 +280,12 @@
         : DBusDelegate(missive::kConfirmRecordUpload,
                        owner,
                        base::DoNothing()) {
-      *request_.mutable_sequence_information() =
-          std::move(sequence_information);
+      *request_.mutable_sequence_information() = sequence_information;
       request_.set_force_confirm(force_confirm);
     }
 
-    void WriteRequest(dbus::MessageWriter* writer) override {
-      writer->AppendProtoAsArrayOfBytes(request_);
+    bool WriteRequest(dbus::MessageWriter* writer) override {
+      return writer->AppendProtoAsArrayOfBytes(request_);
     }
 
    private:
diff --git a/chromeos/network/auto_connect_handler.cc b/chromeos/network/auto_connect_handler.cc
index b1ef7ae..54d2e1c 100644
--- a/chromeos/network/auto_connect_handler.cc
+++ b/chromeos/network/auto_connect_handler.cc
@@ -4,8 +4,6 @@
 
 #include "chromeos/network/auto_connect_handler.h"
 
-#include <sstream>
-
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -47,61 +45,28 @@
   }
 }
 
-void DisconnectErrorCallback(
-    const NetworkState* network,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void DisconnectErrorCallback(const NetworkState* network,
+                             const std::string& error_name) {
   RecordDisconnectByPolicyResult(network, /*success=*/false);
 
-  std::stringstream error_data_ss;
-  if (error_data)
-    error_data_ss << *error_data;
-  else
-    error_data_ss << "<none>";
-
   NET_LOG(ERROR) << "AutoConnectHandler.Disconnect failed for: "
                  << NetworkPathId(network->path())
-                 << " Error name: " << error_name
-                 << ", Data: " << error_data_ss.str();
+                 << " Error name: " << error_name;
 }
 
-void RemoveNetworkConfigurationErrorCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  std::stringstream error_data_ss;
-  if (error_data)
-    error_data_ss << *error_data;
-  else
-    error_data_ss << "<none>";
+void RemoveNetworkConfigurationErrorCallback(const std::string& error_name) {
   NET_LOG(ERROR) << "AutoConnectHandler RemoveNetworkConfiguration failed."
-                 << " Error name: " << error_name
-                 << ", Data: " << error_data_ss.str();
+                 << " Error name: " << error_name;
 }
 
-void ConnectToNetworkErrorCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  std::stringstream error_data_ss;
-  if (error_data)
-    error_data_ss << *error_data;
-  else
-    error_data_ss << "<none>";
+void ConnectToNetworkErrorCallback(const std::string& error_name) {
   NET_LOG(ERROR) << "AutoConnectHandler ConnectToNetwork failed."
-                 << " Error name: " << error_name
-                 << ", Data: " << error_data_ss.str();
+                 << " Error name: " << error_name;
 }
 
-void SetPropertiesErrorCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  std::stringstream error_data_ss;
-  if (error_data)
-    error_data_ss << *error_data;
-  else
-    error_data_ss << "<none>";
+void SetPropertiesErrorCallback(const std::string& error_name) {
   NET_LOG(ERROR) << "AutoConnectHandler SetProperties failed."
-                 << " Error name: " << error_name
-                 << ", Data: " << error_data_ss.str();
+                 << " Error name: " << error_name;
 }
 
 std::string AutoConnectReasonsToString(int auto_connect_reasons) {
diff --git a/chromeos/network/cellular_esim_installer_unittest.cc b/chromeos/network/cellular_esim_installer_unittest.cc
index 1a78ded..2fe00fc 100644
--- a/chromeos/network/cellular_esim_installer_unittest.cc
+++ b/chromeos/network/cellular_esim_installer_unittest.cc
@@ -161,7 +161,7 @@
       EXPECT_LE(1u, network_connection_handler_->connect_calls().size());
       if (fail_connect) {
         network_connection_handler_->connect_calls().back().InvokeErrorCallback(
-            "fake_error_name", /*error_data=*/nullptr);
+            "fake_error_name");
       } else {
         network_connection_handler_->connect_calls()
             .back()
@@ -421,4 +421,4 @@
   CheckInstallSuccess(result_tuple);
 }
 
-}  // namespace chromeos
\ No newline at end of file
+}  // namespace chromeos
diff --git a/chromeos/network/cellular_esim_uninstall_handler.cc b/chromeos/network/cellular_esim_uninstall_handler.cc
index cb6b4347..245a44d 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler.cc
@@ -195,8 +195,7 @@
 }
 
 void CellularESimUninstallHandler::OnDisconnectFailure(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   DCHECK_EQ(state_, UninstallState::kDisconnectingNetwork);
 
   NET_LOG(ERROR) << "Failed disconnecting network for request "
@@ -393,8 +392,7 @@
 }
 
 void CellularESimUninstallHandler::OnRemoveServiceFailure(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   DCHECK_EQ(state_, UninstallState::kRemovingShillService);
   NET_LOG(ERROR) << "Error removing service for request "
                  << *uninstall_requests_.front() << ". Error: " << error_name;
diff --git a/chromeos/network/cellular_esim_uninstall_handler.h b/chromeos/network/cellular_esim_uninstall_handler.h
index bd715109..c6ef23a 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.h
+++ b/chromeos/network/cellular_esim_uninstall_handler.h
@@ -11,7 +11,6 @@
 #include "base/containers/circular_deque.h"
 #include "base/containers/queue.h"
 #include "base/gtest_prod_util.h"
-#include "base/values.h"
 #include "chromeos/dbus/hermes/hermes_response_status.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
 #include "chromeos/network/cellular_inhibitor.h"
@@ -158,8 +157,7 @@
 
   void AttemptNetworkDisconnect(const NetworkState* network);
   void OnDisconnectSuccess();
-  void OnDisconnectFailure(const std::string& error_name,
-                           std::unique_ptr<base::DictionaryValue> error_data);
+  void OnDisconnectFailure(const std::string& error_name);
 
   void AttemptShillInhibit();
   void OnShillInhibit(
@@ -177,9 +175,7 @@
 
   void AttemptRemoveShillService();
   void OnRemoveServiceSuccess();
-  void OnRemoveServiceFailure(
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void OnRemoveServiceFailure(const std::string& error_name);
   void OnNetworkListWaitTimeout();
 
   absl::optional<dbus::ObjectPath> GetEnabledCellularESimProfilePath();
diff --git a/chromeos/network/cellular_esim_uninstall_handler_unittest.cc b/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
index 8c70183..5ce3cb4f 100644
--- a/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler_unittest.cc
@@ -164,7 +164,7 @@
     if (should_fail) {
       network_connection_handler_->disconnect_calls()
           .front()
-          .InvokeErrorCallback("disconnect_error", nullptr);
+          .InvokeErrorCallback("disconnect_error");
     } else {
       network_connection_handler_->disconnect_calls()
           .front()
diff --git a/chromeos/network/cellular_inhibitor.cc b/chromeos/network/cellular_inhibitor.cc
index 77b7b25e..140d2faf 100644
--- a/chromeos/network/cellular_inhibitor.cc
+++ b/chromeos/network/cellular_inhibitor.cc
@@ -285,10 +285,8 @@
   CheckInhibitPropertyIfNeeded();
 }
 
-void CellularInhibitor::OnSetPropertyError(
-    bool attempted_inhibit,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CellularInhibitor::OnSetPropertyError(bool attempted_inhibit,
+                                           const std::string& error_name) {
   NET_LOG(ERROR) << (attempted_inhibit ? "Inhibit" : "Uninhibit")
                  << "CellularScanning() failed: " << error_name;
   ReturnSetInhibitPropertyResult(/*success=*/false,
diff --git a/chromeos/network/cellular_inhibitor.h b/chromeos/network/cellular_inhibitor.h
index e1146c9..0001d82b 100644
--- a/chromeos/network/cellular_inhibitor.h
+++ b/chromeos/network/cellular_inhibitor.h
@@ -167,8 +167,7 @@
   void SetInhibitProperty();
   void OnSetPropertySuccess();
   void OnSetPropertyError(bool attempted_inhibit,
-                          const std::string& error_name,
-                          std::unique_ptr<base::DictionaryValue> error_data);
+                          const std::string& error_name);
   // Returns result of setting inhibit property. |result| is the operation
   // error result and is set only for failures.
   void ReturnSetInhibitPropertyResult(
diff --git a/chromeos/network/fake_network_activation_handler.cc b/chromeos/network/fake_network_activation_handler.cc
index 77f2eea..1d078ad 100644
--- a/chromeos/network/fake_network_activation_handler.cc
+++ b/chromeos/network/fake_network_activation_handler.cc
@@ -5,7 +5,6 @@
 #include "chromeos/network/fake_network_activation_handler.h"
 
 #include "base/callback.h"
-#include "base/values.h"
 
 namespace chromeos {
 
@@ -27,9 +26,8 @@
 }
 
 void FakeNetworkActivationHandler::ActivationParams::InvokeErrorCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  std::move(error_callback_).Run(error_name, std::move(error_data));
+    const std::string& error_name) {
+  std::move(error_callback_).Run(error_name);
 }
 
 FakeNetworkActivationHandler::FakeNetworkActivationHandler() = default;
diff --git a/chromeos/network/fake_network_activation_handler.h b/chromeos/network/fake_network_activation_handler.h
index 109ffdda..08272684 100644
--- a/chromeos/network/fake_network_activation_handler.h
+++ b/chromeos/network/fake_network_activation_handler.h
@@ -39,8 +39,7 @@
     const std::string& service_path() const { return service_path_; }
 
     void InvokeSuccessCallback();
-    void InvokeErrorCallback(const std::string& error_name,
-                             std::unique_ptr<base::DictionaryValue> error_data);
+    void InvokeErrorCallback(const std::string& error_name);
 
    private:
     std::string service_path_;
diff --git a/chromeos/network/fake_network_connection_handler.cc b/chromeos/network/fake_network_connection_handler.cc
index 813d4ce..7b7ffdb 100644
--- a/chromeos/network/fake_network_connection_handler.cc
+++ b/chromeos/network/fake_network_connection_handler.cc
@@ -38,9 +38,8 @@
 }
 
 void FakeNetworkConnectionHandler::ConnectionParams::InvokeErrorCallback(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
-  std::move(error_callback_).Run(error_name, std::move(error_data));
+    const std::string& error_name) {
+  std::move(error_callback_).Run(error_name);
 }
 
 FakeNetworkConnectionHandler::FakeNetworkConnectionHandler() = default;
diff --git a/chromeos/network/fake_network_connection_handler.h b/chromeos/network/fake_network_connection_handler.h
index af5b360..f67bc63 100644
--- a/chromeos/network/fake_network_connection_handler.h
+++ b/chromeos/network/fake_network_connection_handler.h
@@ -56,8 +56,7 @@
     }
 
     void InvokeSuccessCallback();
-    void InvokeErrorCallback(const std::string& error_name,
-                             std::unique_ptr<base::DictionaryValue> error_data);
+    void InvokeErrorCallback(const std::string& error_name);
 
    private:
     std::string service_path_;
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 41d8d60..dbe094d5 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -77,17 +77,14 @@
 void InvokeErrorCallback(const std::string& service_path,
                          network_handler::ErrorCallback error_callback,
                          const std::string& error_name) {
-  std::string error_msg = "ManagedConfig Error: " + error_name;
-  NET_LOG(ERROR) << error_msg << " For: " << NetworkPathId(service_path);
-  network_handler::RunErrorCallback(std::move(error_callback), service_path,
-                                    error_name, error_msg);
+  NET_LOG(ERROR) << "ManagedConfig Error: " << error_name
+                 << " For: " << NetworkPathId(service_path);
+  network_handler::RunErrorCallback(std::move(error_callback), error_name);
 }
 
-void LogErrorWithDictAndCallCallback(
-    base::OnceClosure callback,
-    const base::Location& from_where,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void LogErrorWithDictAndCallCallback(base::OnceClosure callback,
+                                     const base::Location& from_where,
+                                     const std::string& error_name) {
   device_event_log::AddEntry(from_where.file_name(), from_where.line_number(),
                              device_event_log::LOG_TYPE_NETWORK,
                              device_event_log::LOG_LEVEL_ERROR, error_name);
diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc
index 7391ece3..1aca3de 100644
--- a/chromeos/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/network/managed_network_configuration_handler_unittest.cc
@@ -9,7 +9,6 @@
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
@@ -98,18 +97,8 @@
     "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[],"
     "\"Certificates\":[]}";
 
-std::string PrettyJson(const base::Value& value) {
-  std::string pretty;
-  base::JSONWriter::WriteWithOptions(
-      value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
-  return pretty;
-}
-
-void ErrorCallback(const std::string& error_name,
-                   std::unique_ptr<base::DictionaryValue> error_data) {
-  ADD_FAILURE() << "Unexpected error: " << error_name
-                << " with associated data: \n"
-                << PrettyJson(*error_data);
+void ErrorCallback(const std::string& error_name) {
+  ADD_FAILURE() << "Unexpected error: " << error_name;
 }
 
 class TestNetworkProfileHandler : public NetworkProfileHandler {
diff --git a/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc b/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc
index 57f94f5..e77c2a72 100644
--- a/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc
+++ b/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/callback.h"
-#include "base/json/json_writer.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
@@ -38,18 +37,8 @@
 const char kVpnHistogramConfigurationSourceWireGuard[] =
     "Network.Ash.VPN.WireGuard.ConfigurationSource";
 
-std::string PrettyJson(const base::DictionaryValue& value) {
-  std::string pretty;
-  base::JSONWriter::WriteWithOptions(
-      value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
-  return pretty;
-}
-
-void ErrorCallback(const std::string& error_name,
-                   std::unique_ptr<base::DictionaryValue> error_data) {
-  ADD_FAILURE() << "Unexpected error: " << error_name
-                << " with associated data: \n"
-                << PrettyJson(*error_data);
+void ErrorCallback(const std::string& error_name) {
+  ADD_FAILURE() << "Unexpected error: " << error_name;
 }
 
 }  // namespace
diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc
index 792d3e6..783b4cc1 100644
--- a/chromeos/network/network_configuration_handler.cc
+++ b/chromeos/network/network_configuration_handler.cc
@@ -45,10 +45,9 @@
 void InvokeErrorCallback(const std::string& service_path,
                          network_handler::ErrorCallback error_callback,
                          const std::string& error_name) {
-  std::string error_msg = "Config Error: " + error_name;
-  NET_LOG(ERROR) << error_msg << " For: " << NetworkPathId(service_path);
-  network_handler::RunErrorCallback(std::move(error_callback), service_path,
-                                    error_name, error_msg);
+  NET_LOG(ERROR) << "Config Error: " << error_name
+                 << " For: " << NetworkPathId(service_path);
+  network_handler::RunErrorCallback(std::move(error_callback), error_name);
 }
 
 void SetNetworkProfileErrorCallback(
diff --git a/chromeos/network/network_configuration_handler_unittest.cc b/chromeos/network/network_configuration_handler_unittest.cc
index d55e9502..1d77e57 100644
--- a/chromeos/network/network_configuration_handler_unittest.cc
+++ b/chromeos/network/network_configuration_handler_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
@@ -59,23 +58,11 @@
   *guid_out = guid;
 }
 
-std::string PrettyJson(const base::DictionaryValue& value) {
-  std::string pretty;
-  base::JSONWriter::WriteWithOptions(
-      value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
-  return pretty;
+void ErrorCallback(const std::string& error_name) {
+  ADD_FAILURE() << "Unexpected error: " << error_name;
 }
 
-void ErrorCallback(const std::string& error_name,
-                   std::unique_ptr<base::DictionaryValue> error_data) {
-  ADD_FAILURE() << "Unexpected error: " << error_name
-                << " with associated data: \n"
-                << PrettyJson(*error_data);
-}
-
-void RecordError(std::string* error_name_ptr,
-                 const std::string& error_name,
-                 std::unique_ptr<base::DictionaryValue> error_data) {
+void RecordError(std::string* error_name_ptr, const std::string& error_name) {
   *error_name_ptr = error_name;
 }
 
diff --git a/chromeos/network/network_connect.cc b/chromeos/network/network_connect.cc
index 810ac1f9..374037ef 100644
--- a/chromeos/network/network_connect.cc
+++ b/chromeos/network/network_connect.cc
@@ -29,8 +29,7 @@
 
 namespace {
 
-void IgnoreDisconnectError(const std::string& error_name,
-                           std::unique_ptr<base::DictionaryValue> error_data) {}
+void IgnoreDisconnectError(const std::string& error_name) {}
 
 const NetworkState* GetNetworkStateFromId(const std::string& network_id) {
   // Note: network_id === NetworkState::guid.
@@ -79,14 +78,12 @@
   void ActivateCellular(const std::string& network_id);
   void HandleUnconfiguredNetwork(const std::string& network_id);
   void OnConnectFailed(const std::string& network_id,
-                       const std::string& error_name,
-                       std::unique_ptr<base::DictionaryValue> error_data);
+                       const std::string& error_name);
   bool GetNetworkProfilePath(bool shared, std::string* profile_path);
   void OnConnectSucceeded(const std::string& network_id);
   void CallConnectToNetwork(const std::string& network_id,
                             bool check_error_state);
-  void OnConfigureFailed(const std::string& error_name,
-                         std::unique_ptr<base::DictionaryValue> error_data);
+  void OnConfigureFailed(const std::string& error_name);
   void OnConfigureSucceeded(bool connect_on_configure,
                             const std::string& service_path,
                             const std::string& network_id);
@@ -95,8 +92,7 @@
                                bool connect_on_configure);
   void SetPropertiesFailed(const std::string& desc,
                            const std::string& network_id,
-                           const std::string& config_error_name,
-                           std::unique_ptr<base::DictionaryValue> error_data);
+                           const std::string& config_error_name);
   void SetPropertiesToClear(base::DictionaryValue* properties_to_set,
                             std::vector<std::string>* properties_to_clear);
   void ClearPropertiesAndConnect(
@@ -194,10 +190,8 @@
   return true;
 }
 
-void NetworkConnectImpl::OnConnectFailed(
-    const std::string& network_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void NetworkConnectImpl::OnConnectFailed(const std::string& network_id,
+                                         const std::string& error_name) {
   NET_LOG(ERROR) << "Connect Failed: " << error_name
                  << " For: " << NetworkGuidId(network_id);
   if (error_name == NetworkConnectionHandler::kErrorConnectFailed ||
@@ -228,8 +222,7 @@
                                               bool check_error_state) {
   const NetworkState* network = GetNetworkStateFromId(network_id);
   if (!network) {
-    OnConnectFailed(network_id, NetworkConnectionHandler::kErrorNotFound,
-                    nullptr);
+    OnConnectFailed(network_id, NetworkConnectionHandler::kErrorNotFound);
     return;
   }
 
@@ -242,9 +235,7 @@
       check_error_state, ConnectCallbackMode::ON_COMPLETED);
 }
 
-void NetworkConnectImpl::OnConfigureFailed(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void NetworkConnectImpl::OnConfigureFailed(const std::string& error_name) {
   NET_LOG(ERROR) << "Unable to configure network";
   delegate_->ShowNetworkConnectError(
       NetworkConnectionHandler::kErrorConfigureFailed, "");
@@ -285,8 +276,7 @@
 void NetworkConnectImpl::SetPropertiesFailed(
     const std::string& desc,
     const std::string& network_id,
-    const std::string& config_error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& config_error_name) {
   NET_LOG(ERROR) << desc << ": Failed: " << config_error_name
                  << "For: " << NetworkGuidId(network_id);
   delegate_->ShowNetworkConnectError(
@@ -317,7 +307,7 @@
   const NetworkState* network = GetNetworkStateFromId(network_id);
   if (!network) {
     SetPropertiesFailed("ClearProperties", network_id,
-                        NetworkConnectionHandler::kErrorNotFound, nullptr);
+                        NetworkConnectionHandler::kErrorNotFound);
     return;
   }
   // After configuring a network, ignore any (possibly stale) error state.
@@ -339,7 +329,7 @@
   const NetworkState* network = GetNetworkStateFromId(network_id);
   if (!network) {
     SetPropertiesFailed("SetProperties", network_id,
-                        NetworkConnectionHandler::kErrorNotFound, nullptr);
+                        NetworkConnectionHandler::kErrorNotFound);
     return;
   }
   NetworkHandler::Get()->network_configuration_handler()->SetShillProperties(
@@ -357,8 +347,7 @@
   NET_LOG(USER) << "ConnectToNetwork: " << NetworkGuidId(network_id);
   const NetworkState* network = GetNetworkStateFromId(network_id);
   if (!network) {
-    OnConnectFailed(network_id, NetworkConnectionHandler::kErrorNotFound,
-                    nullptr);
+    OnConnectFailed(network_id, NetworkConnectionHandler::kErrorNotFound);
     return;
   }
   if (PreviousConnectAttemptHadError(network)) {
diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc
index d5241334..f9294d8 100644
--- a/chromeos/network/network_connection_handler.cc
+++ b/chromeos/network/network_connection_handler.cc
@@ -112,8 +112,7 @@
     const std::string& error_name) {
   NET_LOG(ERROR) << "Connect Failure: " << error_name << " for "
                  << NetworkPathId(service_path);
-  network_handler::RunErrorCallback(std::move(error_callback), service_path,
-                                    error_name, "");
+  network_handler::RunErrorCallback(std::move(error_callback), error_name);
   for (auto& observer : observers_)
     observer.ConnectFailed(service_path, error_name);
 }
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc
index 1967623..e78d808 100644
--- a/chromeos/network/network_connection_handler_impl.cc
+++ b/chromeos/network/network_connection_handler_impl.cc
@@ -458,8 +458,8 @@
   if (!network) {
     NET_LOG(ERROR) << "Disconnect Error: Not Found: "
                    << NetworkPathId(service_path);
-    network_handler::RunErrorCallback(std::move(error_callback), service_path,
-                                      kErrorNotFound, "");
+    network_handler::RunErrorCallback(std::move(error_callback),
+                                      kErrorNotFound);
     return;
   }
   const std::string connection_state = network->connection_state();
@@ -467,8 +467,8 @@
       !NetworkState::StateIsConnecting(connection_state) &&
       !GetPendingRequest(service_path)) {
     NET_LOG(ERROR) << "Disconnect Error: Not Connected: " << NetworkId(network);
-    network_handler::RunErrorCallback(std::move(error_callback), service_path,
-                                      kErrorNotConnected, "");
+    network_handler::RunErrorCallback(std::move(error_callback),
+                                      kErrorNotConnected);
     return;
   }
   if (NetworkTypePattern::Tether().MatchesType(network->type())) {
@@ -603,8 +603,7 @@
     const std::string& service_path,
     absl::optional<base::Value> properties) {
   if (!properties) {
-    HandleConfigurationFailure(service_path, "GetShillProperties failed",
-                               nullptr);
+    HandleConfigurationFailure(service_path, "GetShillProperties failed");
     return;
   }
   NET_LOG(EVENT) << "VerifyConfiguredAndConnect: "
@@ -623,8 +622,7 @@
 
   const std::string* type = properties->FindStringKey(shill::kTypeProperty);
   if (!type) {
-    HandleConfigurationFailure(service_path, "Properties with no type",
-                               nullptr);
+    HandleConfigurationFailure(service_path, "Properties with no type");
     return;
   }
   bool connectable =
@@ -902,8 +900,7 @@
 
 void NetworkConnectionHandlerImpl::HandleConfigurationFailure(
     const std::string& service_path,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   NET_LOG(ERROR) << "Connect configuration failure: " << error_name
                  << " for: " << NetworkPathId(service_path);
   ConnectRequest* request = GetPendingRequest(service_path);
diff --git a/chromeos/network/network_connection_handler_impl.h b/chromeos/network/network_connection_handler_impl.h
index b1890bb6..eb0622b 100644
--- a/chromeos/network/network_connection_handler_impl.h
+++ b/chromeos/network/network_connection_handler_impl.h
@@ -119,10 +119,8 @@
   void CallShillConnect(const std::string& service_path);
 
   // Handles failure from ConfigurationHandler calls.
-  void HandleConfigurationFailure(
-      const std::string& service_path,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void HandleConfigurationFailure(const std::string& service_path,
+                                  const std::string& error_name);
 
   // Handles success or failure from Shill.Service.Connect.
   void HandleShillConnectSuccess(const std::string& service_path);
diff --git a/chromeos/network/network_connection_handler_impl_unittest.cc b/chromeos/network/network_connection_handler_impl_unittest.cc
index e311b72..3408271 100644
--- a/chromeos/network/network_connection_handler_impl_unittest.cc
+++ b/chromeos/network/network_connection_handler_impl_unittest.cc
@@ -288,10 +288,7 @@
 
   void SuccessCallback() { result_ = kSuccessResult; }
 
-  void ErrorCallback(const std::string& error_name,
-                     std::unique_ptr<base::DictionaryValue> error_data) {
-    result_ = error_name;
-  }
+  void ErrorCallback(const std::string& error_name) { result_ = error_name; }
 
   std::string GetResultAndReset() {
     std::string result;
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc
index 5286c0b..5ef5163 100644
--- a/chromeos/network/network_device_handler_impl.cc
+++ b/chromeos/network/network_device_handler_impl.cc
@@ -68,10 +68,8 @@
 void InvokeErrorCallback(const std::string& device_path,
                          network_handler::ErrorCallback error_callback,
                          const std::string& error_name) {
-  std::string error_msg = "Device Error: " + error_name;
-  NET_LOG(ERROR) << error_msg << ": " << device_path;
-  network_handler::RunErrorCallback(std::move(error_callback), device_path,
-                                    error_name, error_msg);
+  NET_LOG(ERROR) << "Device Error: " << error_name << ": " << device_path;
+  network_handler::RunErrorCallback(std::move(error_callback), error_name);
 }
 
 void HandleShillCallFailure(const std::string& device_path,
diff --git a/chromeos/network/network_device_handler_unittest.cc b/chromeos/network/network_device_handler_unittest.cc
index 58d11c9..fe6af94a 100644
--- a/chromeos/network/network_device_handler_unittest.cc
+++ b/chromeos/network/network_device_handler_unittest.cc
@@ -93,8 +93,7 @@
                           base::Unretained(this));
   }
 
-  void ErrorCallback(const std::string& error_name,
-                     std::unique_ptr<base::DictionaryValue> error_data) {
+  void ErrorCallback(const std::string& error_name) {
     VLOG(1) << "ErrorCallback: " << error_name;
     result_ = error_name;
   }
diff --git a/chromeos/network/network_handler_callbacks.cc b/chromeos/network/network_handler_callbacks.cc
index 925c578..8ef97ab 100644
--- a/chromeos/network/network_handler_callbacks.cc
+++ b/chromeos/network/network_handler_callbacks.cc
@@ -24,42 +24,14 @@
 namespace chromeos {
 namespace network_handler {
 
-// None of these messages are user-facing, they should only appear in logs.
+// This message is not user-facing, it should only appear in logs.
 const char kDBusFailedError[] = "Error.DBusFailed";
-const char kDBusFailedErrorMessage[] = "DBus call failed.";
-
-// These are names of fields in the error data dictionary for ErrorCallback.
-const char kErrorName[] = "errorName";
-const char kErrorDetail[] = "errorDetail";
-const char kDbusErrorName[] = "dbusErrorName";
-const char kDbusErrorMessage[] = "dbusErrorMessage";
-const char kPath[] = "path";
 
 void RunErrorCallback(ErrorCallback error_callback,
-                      const std::string& path,
-                      const std::string& error_name,
-                      const std::string& error_detail) {
+                      const std::string& error_name) {
   if (error_callback.is_null())
     return;
-  std::move(error_callback)
-      .Run(error_name,
-           CreateDBusErrorData(path, error_name, error_detail, "", ""));
-}
-
-std::unique_ptr<base::DictionaryValue> CreateDBusErrorData(
-    const std::string& path,
-    const std::string& error_name,
-    const std::string& error_detail,
-    const std::string& dbus_error_name,
-    const std::string& dbus_error_message) {
-  auto error_data = std::make_unique<base::DictionaryValue>();
-  error_data->SetString(kErrorName, error_name);
-  error_data->SetString(kErrorDetail, error_detail);
-  error_data->SetString(kDbusErrorName, dbus_error_name);
-  error_data->SetString(kDbusErrorMessage, dbus_error_message);
-  if (!path.empty())
-    error_data->SetString(kPath, path);
-  return error_data;
+  std::move(error_callback).Run(error_name);
 }
 
 void ShillErrorCallbackFunction(const std::string& error_name,
@@ -80,10 +52,7 @@
 
   if (error_callback.is_null())
     return;
-  std::move(error_callback)
-      .Run(error_name,
-           CreateDBusErrorData(path, error_name, detail, dbus_error_name,
-                               dbus_error_message));
+  std::move(error_callback).Run(error_name);
 }
 
 }  // namespace network_handler
diff --git a/chromeos/network/network_handler_callbacks.h b/chromeos/network/network_handler_callbacks.h
index 64760ff..45219930 100644
--- a/chromeos/network/network_handler_callbacks.h
+++ b/chromeos/network/network_handler_callbacks.h
@@ -18,11 +18,6 @@
 namespace network_handler {
 
 COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kDBusFailedError[];
-COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kDBusFailedErrorMessage[];
-COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kErrorName[];
-COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kErrorDetail[];
-COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kDbusErrorName[];
-COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kDbusErrorMessage[];
 
 // On success, |result| contains the result. On failure, |result| is nullopt.
 using ResultCallback =
@@ -39,35 +34,17 @@
 
 // An error callback used by both the configuration handler and the state
 // handler to receive error results from the API.
-using ErrorCallback =
-    base::OnceCallback<void(const std::string& error_name,
-                            std::unique_ptr<base::DictionaryValue> error_data)>;
+using ErrorCallback = base::OnceCallback<void(const std::string& error_name)>;
 
 using ServiceResultCallback =
     base::OnceCallback<void(const std::string& service_path,
                             const std::string& guid)>;
 
-// Create a DictionaryValue for passing to ErrorCallback.
-COMPONENT_EXPORT(CHROMEOS_NETWORK)
-base::DictionaryValue* CreateErrorData(const std::string& path,
-                                       const std::string& error_name,
-                                       const std::string& error_detail);
-
 // If not NULL, runs |error_callback| with an ErrorData dictionary created from
 // the other arguments.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
 void RunErrorCallback(ErrorCallback error_callback,
-                      const std::string& path,
-                      const std::string& error_name,
-                      const std::string& error_detail);
-
-COMPONENT_EXPORT(CHROMEOS_NETWORK)
-std::unique_ptr<base::DictionaryValue> CreateDBusErrorData(
-    const std::string& path,
-    const std::string& error_name,
-    const std::string& error_detail,
-    const std::string& dbus_error_name,
-    const std::string& dbus_error_message);
+                      const std::string& error_name);
 
 // Callback for Shill errors.
 // |error_name| is the error name passed to |error_callback|.
diff --git a/chromeos/network/network_metadata_store.cc b/chromeos/network/network_metadata_store.cc
index 22bf52bc..3dc1db6 100644
--- a/chromeos/network/network_metadata_store.cc
+++ b/chromeos/network/network_metadata_store.cc
@@ -222,9 +222,7 @@
   return profile_pref_service_->GetBoolean(kHasFixedHiddenNetworks);
 }
 
-void NetworkMetadataStore::OnDisableHiddenError(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void NetworkMetadataStore::OnDisableHiddenError(const std::string& error_name) {
   NET_LOG(EVENT) << "Failed to disable HiddenSSID on synced network. Error: "
                  << error_name;
 }
diff --git a/chromeos/network/network_metadata_store.h b/chromeos/network/network_metadata_store.h
index ce6bea1..bf445bf6 100644
--- a/chromeos/network/network_metadata_store.h
+++ b/chromeos/network/network_metadata_store.h
@@ -153,8 +153,7 @@
 
   // Sets the owner metadata when there is an active user, otherwise a no-op.
   void SetIsCreatedByUser(const std::string& network_guid);
-  void OnDisableHiddenError(const std::string& error_name,
-                            std::unique_ptr<base::DictionaryValue> error_data);
+  void OnDisableHiddenError(const std::string& error_name);
 
   base::ObserverList<NetworkMetadataObserver> observers_;
   NetworkConfigurationHandler* network_configuration_handler_;
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 2cf62c4..7b7f88d 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -323,9 +323,8 @@
                        << "DeviceState, but the current state was: "
                        << tether_technology_state_;
         network_handler::RunErrorCallback(
-            std::move(error_callback), kTetherDevicePath,
-            NetworkConnectionHandler::kErrorEnabledOrDisabledWhenNotAvailable,
-            "");
+            std::move(error_callback),
+            NetworkConnectionHandler::kErrorEnabledOrDisabledWhenNotAvailable);
         continue;
       }
 
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
index 423c16bc..45de35fc 100644
--- a/chromeos/network/shill_property_handler.cc
+++ b/chromeos/network/shill_property_handler.cc
@@ -162,10 +162,10 @@
   if (enabled) {
     if (prohibited_technologies_.find(technology) !=
         prohibited_technologies_.end()) {
-      chromeos::network_handler::RunErrorCallback(
-          std::move(error_callback), "", "prohibited_technologies",
-          "Ignored: Attempt to enable prohibited network technology " +
-              technology);
+      NET_LOG(ERROR) << "Attempt to enable prohibited network technology: "
+                     << technology;
+      chromeos::network_handler::RunErrorCallback(std::move(error_callback),
+                                                  "prohibited_technologies");
       NetworkMetricsHelper::LogEnableTechnologyResult(technology,
                                                       /*success=*/false);
       return;
diff --git a/chromeos/services/cellular_setup/esim_profile_unittest.cc b/chromeos/services/cellular_setup/esim_profile_unittest.cc
index 03e5c67..e0dfa203 100644
--- a/chromeos/services/cellular_setup/esim_profile_unittest.cc
+++ b/chromeos/services/cellular_setup/esim_profile_unittest.cc
@@ -32,7 +32,7 @@
 
 class TestUserManager : public user_manager::FakeUserManager {
  public:
-  TestUserManager(bool is_guest) : is_guest_(is_guest) {
+  explicit TestUserManager(bool is_guest) : is_guest_(is_guest) {
     user_manager::UserManager::SetInstance(this);
   }
   ~TestUserManager() override = default;
@@ -148,7 +148,7 @@
         network_connection_handler()
             ->connect_calls()
             .back()
-            .InvokeErrorCallback("fake_error_name", /*error_data=*/nullptr);
+            .InvokeErrorCallback("fake_error_name");
       } else {
         network_connection_handler()
             ->connect_calls()
diff --git a/chromeos/services/cellular_setup/euicc_unittest.cc b/chromeos/services/cellular_setup/euicc_unittest.cc
index b86a70a9..4507bba 100644
--- a/chromeos/services/cellular_setup/euicc_unittest.cc
+++ b/chromeos/services/cellular_setup/euicc_unittest.cc
@@ -101,7 +101,7 @@
         network_connection_handler()
             ->connect_calls()
             .back()
-            .InvokeErrorCallback("fake_error_name", /*error_data=*/nullptr);
+            .InvokeErrorCallback("fake_error_name");
       } else {
         network_connection_handler()
             ->connect_calls()
@@ -292,4 +292,4 @@
 }
 
 }  // namespace cellular_setup
-}  // namespace chromeos
\ No newline at end of file
+}  // namespace chromeos
diff --git a/chromeos/services/cellular_setup/ota_activator_impl.cc b/chromeos/services/cellular_setup/ota_activator_impl.cc
index 6484365b..36ca457 100644
--- a/chromeos/services/cellular_setup/ota_activator_impl.cc
+++ b/chromeos/services/cellular_setup/ota_activator_impl.cc
@@ -379,15 +379,12 @@
 }
 
 void OtaActivatorImpl::OnCompleteActivationError(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   NET_LOG(ERROR) << "CompleteActivation() failed. Error name: " << error_name;
   FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
 }
 
-void OtaActivatorImpl::OnNetworkConnectionError(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void OtaActivatorImpl::OnNetworkConnectionError(const std::string& error_name) {
   if (connect_retry_attempts_ >= kMaxConnectRetryAttempt) {
     NET_LOG(ERROR) << "Reached max connection retry attempts.";
     FinishActivationAttempt(mojom::ActivationResult::kFailedToActivate);
diff --git a/chromeos/services/cellular_setup/ota_activator_impl.h b/chromeos/services/cellular_setup/ota_activator_impl.h
index ae14f38c..1f67d1ccb 100644
--- a/chromeos/services/cellular_setup/ota_activator_impl.h
+++ b/chromeos/services/cellular_setup/ota_activator_impl.h
@@ -18,10 +18,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace chromeos {
 
 class NetworkActivationHandler;
@@ -125,12 +121,8 @@
   void AttemptToSendMetadataToDelegate();
   void AttemptToCompleteActivation();
 
-  void OnCompleteActivationError(
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
-  void OnNetworkConnectionError(
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void OnCompleteActivationError(const std::string& error_name);
+  void OnNetworkConnectionError(const std::string& error_name);
 
   void FlushForTesting();
 
diff --git a/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc b/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
index f24769a..f8f0e3d 100644
--- a/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
+++ b/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
@@ -213,7 +213,7 @@
     EXPECT_EQ(kTestCellularServicePath, connect_calls.back().service_path());
     EXPECT_EQ(ConnectCallbackMode::ON_COMPLETED,
               connect_calls.back().connect_callback_mode());
-    connect_calls.back().InvokeErrorCallback("fake_error", nullptr);
+    connect_calls.back().InvokeErrorCallback("fake_error");
     base::RunLoop().RunUntilIdle();
   }
 
@@ -232,8 +232,7 @@
     if (success) {
       complete_activation_calls[0].InvokeSuccessCallback();
     } else {
-      complete_activation_calls[0].InvokeErrorCallback(
-          "error", nullptr /* error_data */);
+      complete_activation_calls[0].InvokeErrorCallback("error");
     }
   }
 
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index a6bd4003..bf56739 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -2503,11 +2503,9 @@
   set_properties_callbacks_.erase(iter);
 }
 
-void CrosNetworkConfig::SetPropertiesFailure(
-    const std::string& guid,
-    int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CrosNetworkConfig::SetPropertiesFailure(const std::string& guid,
+                                             int callback_id,
+                                             const std::string& error_name) {
   auto iter = set_properties_callbacks_.find(callback_id);
   DCHECK(iter != set_properties_callbacks_.end());
   NET_LOG(ERROR) << "Failed to set network properties: " << guid
@@ -2564,10 +2562,8 @@
   configure_network_callbacks_.erase(iter);
 }
 
-void CrosNetworkConfig::ConfigureNetworkFailure(
-    int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CrosNetworkConfig::ConfigureNetworkFailure(int callback_id,
+                                                const std::string& error_name) {
   auto iter = configure_network_callbacks_.find(callback_id);
   DCHECK(iter != configure_network_callbacks_.end());
   DCHECK(iter->second);
@@ -2633,11 +2629,9 @@
   forget_network_callbacks_.erase(iter);
 }
 
-void CrosNetworkConfig::ForgetNetworkFailure(
-    const std::string& guid,
-    int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CrosNetworkConfig::ForgetNetworkFailure(const std::string& guid,
+                                             int callback_id,
+                                             const std::string& error_name) {
   auto iter = forget_network_callbacks_.find(callback_id);
   DCHECK(iter != forget_network_callbacks_.end());
   NET_LOG(ERROR) << "Failed to forget network: " << guid
@@ -2752,8 +2746,7 @@
 
 void CrosNetworkConfig::SetCellularSimStateFailure(
     int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   auto iter = set_cellular_sim_state_callbacks_.find(callback_id);
   DCHECK(iter != set_cellular_sim_state_callbacks_.end());
   std::move(iter->second).Run(false);
@@ -2796,8 +2789,7 @@
 
 void CrosNetworkConfig::SelectCellularMobileNetworkFailure(
     int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   auto iter = select_cellular_mobile_network_callbacks_.find(callback_id);
   DCHECK(iter != select_cellular_mobile_network_callbacks_.end());
   std::move(iter->second).Run(false);
@@ -2944,10 +2936,8 @@
   start_connect_callbacks_.erase(iter);
 }
 
-void CrosNetworkConfig::StartConnectFailure(
-    int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CrosNetworkConfig::StartConnectFailure(int callback_id,
+                                            const std::string& error_name) {
   auto iter = start_connect_callbacks_.find(callback_id);
   DCHECK(iter != start_connect_callbacks_.end());
   mojom::StartConnectResult result;
@@ -3004,10 +2994,8 @@
   start_disconnect_callbacks_.erase(iter);
 }
 
-void CrosNetworkConfig::StartDisconnectFailure(
-    int callback_id,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+void CrosNetworkConfig::StartDisconnectFailure(int callback_id,
+                                               const std::string& error_name) {
   auto iter = start_disconnect_callbacks_.find(callback_id);
   DCHECK(iter != start_disconnect_callbacks_.end());
   std::move(iter->second).Run(false);
diff --git a/chromeos/services/network_config/cros_network_config.h b/chromeos/services/network_config/cros_network_config.h
index cc5896e..9aebd922 100644
--- a/chromeos/services/network_config/cros_network_config.h
+++ b/chromeos/services/network_config/cros_network_config.h
@@ -17,10 +17,6 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace chromeos {
 
 class CellularESimProfileHandler;
@@ -129,44 +125,30 @@
                                      const std::string& guid);
   void SetPropertiesFailure(const std::string& guid,
                             int callback_id,
-                            const std::string& error_name,
-                            std::unique_ptr<base::DictionaryValue> error_data);
+                            const std::string& error_name);
   void ConfigureNetworkSuccess(int callback_id,
                                const std::string& service_path,
                                const std::string& guid);
-  void ConfigureNetworkFailure(
-      int callback_id,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void ConfigureNetworkFailure(int callback_id, const std::string& error_name);
   void ForgetNetworkSuccess(int callback_id);
   void ForgetNetworkFailure(const std::string& guid,
                             int callback_id,
-                            const std::string& error_name,
-                            std::unique_ptr<base::DictionaryValue> error_data);
+                            const std::string& error_name);
   void SetCellularSimStateSuccess(int callback_id);
-  void SetCellularSimStateFailure(
-      int callback_id,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void SetCellularSimStateFailure(int callback_id,
+                                  const std::string& error_name);
   void SelectCellularMobileNetworkSuccess(int callback_id);
-  void SelectCellularMobileNetworkFailure(
-      int callback_id,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void SelectCellularMobileNetworkFailure(int callback_id,
+                                          const std::string& error_name);
   void UpdateCustomAPNList(const NetworkState* network,
                            const mojom::ConfigProperties* properties);
   std::vector<mojom::ApnPropertiesPtr> GetCustomAPNList(
       const std::string& guid);
 
   void StartConnectSuccess(int callback_id);
-  void StartConnectFailure(int callback_id,
-                           const std::string& error_name,
-                           std::unique_ptr<base::DictionaryValue> error_data);
+  void StartConnectFailure(int callback_id, const std::string& error_name);
   void StartDisconnectSuccess(int callback_id);
-  void StartDisconnectFailure(
-      int callback_id,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void StartDisconnectFailure(int callback_id, const std::string& error_name);
   void OnGetAlwaysOnVpn(GetAlwaysOnVpnCallback callback,
                         std::string mode,
                         std::string service_path);
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index eff3d95..9c1bbe01 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -72,7 +72,26 @@
       weak_ptr_factory_.GetWeakPtr()));
 }
 
-void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) {}
+void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) {
+  payments::PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails
+      request_details;
+
+  // Unenroll can only happen from the settings page.
+  request_details.virtual_card_enrollment_source =
+      VirtualCardEnrollmentSource::kSettingsPage;
+
+  request_details.virtual_card_enrollment_request_type =
+      VirtualCardEnrollmentRequestType::kUnenroll;
+  request_details.billing_customer_number =
+      payments::GetBillingCustomerId(personal_data_manager_);
+  request_details.instrument_id = instrument_id;
+
+  payments_client_->UpdateVirtualCardEnrollment(
+      request_details,
+      base::BindOnce(&VirtualCardEnrollmentManager::
+                         OnDidGetUpdateVirtualCardEnrollmentResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
 
 bool VirtualCardEnrollmentManager::IsVirtualCardEnrollmentBlocked(
     const std::string& guid) const {
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index 1f46b80..4513069 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -233,4 +233,27 @@
   }
 }
 
+TEST_F(VirtualCardEnrollmentManagerTest, Unenroll) {
+  personal_data_manager_->SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
+
+  virtual_card_enrollment_manager_->Unenroll(
+      /*instrument_id=*/9223372036854775807);
+
+  payments::PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails
+      request_details =
+          payments_client_->update_virtual_card_enrollment_request_details();
+  EXPECT_EQ(request_details.virtual_card_enrollment_source,
+            VirtualCardEnrollmentSource::kSettingsPage);
+  EXPECT_EQ(request_details.virtual_card_enrollment_request_type,
+            VirtualCardEnrollmentRequestType::kUnenroll);
+  EXPECT_EQ(request_details.billing_customer_number, 123456);
+  EXPECT_EQ(request_details.instrument_id, 9223372036854775807);
+
+  // The request should not include a context token, and should succeed.
+  EXPECT_FALSE(request_details.vcn_context_token.has_value());
+  EXPECT_EQ(virtual_card_enrollment_manager_->GetPaymentsRpcResult(),
+            AutofillClient::PaymentsRpcResult::kSuccess);
+}
+
 }  // namespace autofill
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 9b8def64..1d2ad99 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -199,7 +199,6 @@
     "java/res/drawable/bottom_sheet_background.xml",
     "java/res/drawable/card_with_corners_background.xml",
     "java/res/drawable/dialog_bg_no_shadow.xml",
-    "java/res/drawable/hairline_border_card_background.xml",
     "java/res/drawable/ic_arrow_back_24dp.xml",
     "java/res/drawable/ic_check_googblue_24dp_animated.xml",
     "java/res/drawable/ic_offline_pin_blue_white.xml",
diff --git a/components/browser_ui/widget/android/java/res/drawable/hairline_border_card_background.xml b/components/browser_ui/widget/android/java/res/drawable/hairline_border_card_background.xml
deleted file mode 100644
index 8518b45..0000000
--- a/components/browser_ui/widget/android/java/res/drawable/hairline_border_card_background.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 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. -->
-
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle" >
-    <solid android:color="@color/default_bg_color" />
-    <stroke android:width="1dp" android:color="@macro/hairline_stroke_color"/>
-    <corners android:radius="@dimen/default_rounded_corner_radius" />
-</shape>
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
index c1e25d8..df7544da 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -96,9 +96,9 @@
   }
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ChromeCTPolicyEnforcerTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeCTPolicyEnforcerTestBothPolicies,
                            IsLogDisqualifiedTimestamp);
-  FRIEND_TEST_ALL_PREFIXES(ChromeCTPolicyEnforcerTest,
+  FRIEND_TEST_ALL_PREFIXES(ChromeCTPolicyEnforcerTestBothPolicies,
                            IsLogDisqualifiedReturnsFalseOnUnknownLog);
   // Returns true if the log identified by |log_id| (the SHA-256 hash of the
   // log's DER-encoded SPKI) has been disqualified, and sets
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index 31669b1..a6628a22 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -134,15 +134,41 @@
     return result;
   }
 
+  void FillOperatorHistoryWithDiverseOperators(
+      SCTList scts,
+      std::map<std::string, OperatorHistoryEntry>* operator_history) {
+    for (size_t i = 0; i < scts.size(); i++) {
+      OperatorHistoryEntry entry;
+      entry.current_operator_ = "Operator " + base::NumberToString(i);
+      (*operator_history)[scts[i]->log_id] = entry;
+    }
+  }
+
  protected:
   base::SimpleTestClock clock_;
-  std::unique_ptr<net::CTPolicyEnforcer> policy_enforcer_;
+  std::unique_ptr<ChromeCTPolicyEnforcer> policy_enforcer_;
   scoped_refptr<X509Certificate> chain_;
   std::string google_log_id_;
   std::string non_google_log_id_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(ChromeCTPolicyEnforcerTest,
+// Subclass of ChromeCTPolicyEnforcerTest that tests using only the pre-2022
+// policy.
+class ChromeCTPolicyEnforcerTestPre2022Policy
+    : public ChromeCTPolicyEnforcerTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features*/ {},
+        /*disabled_features*/ {
+            features::kCertificateTransparency2022Policy,
+            features::kCertificateTransparency2022PolicyAllCerts});
+    ChromeCTPolicyEnforcerTest::SetUp();
+  }
+};
+
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
        DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
   SCTList scts;
   std::vector<std::string> desired_log_ids(2, google_log_id_);
@@ -156,7 +182,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
        DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
   SCTList scts;
   std::vector<std::string> desired_log_ids(2, non_google_log_id_);
@@ -170,7 +196,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
        ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
   SCTList scts;
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
@@ -183,212 +209,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest, EnforcementDisabledByBinaryAge) {
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-
-  clock_.Advance(base::Days(71));
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
-       ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
-  SCTList scts;
-  std::vector<std::string> desired_logs;
-
-  // One Google log, delivered via OCSP.
-  desired_logs.clear();
-  desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  // One non-Google log, delivered via TLS.
-  desired_logs.clear();
-  desired_logs.push_back(non_google_log_id_);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithPooledEmbeddedSCTs) {
-  SCTList scts;
-  std::vector<std::string> desired_logs;
-
-  // One Google log, delivered embedded.
-  desired_logs.clear();
-  desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  // One non-Google log, delivered via OCSP.
-  desired_logs.clear();
-  desired_logs.push_back(non_google_log_id_);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
-  SCTList scts;
-
-  // The results should be the same before and after disqualification,
-  // regardless of the delivery method.
-
-  // SCT from before disqualification.
-  scts.clear();
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                        false, &scts);
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-
-  // SCT from after disqualification.
-  scts.clear();
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                        true, &scts);
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-
-  // Embedded SCT from before disqualification.
-  scts.clear();
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-
-  // Embedded SCT from after disqualification.
-  scts.clear();
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
-       ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
-
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
-       DoesNotConformWithDisqualifiedLogAfterDisqualificationDate) {
-  SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
-
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
-       DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
-  SCTList scts;
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
-  // Make sure all SCTs are after the disqualification date.
-  for (size_t i = 1; i < scts.size(); ++i)
-    scts[i]->timestamp = scts[0]->timestamp;
-
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
-       DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
-  SCTList scts;
-  std::vector<std::string> desired_logs;
-
-  // One Google Log.
-  desired_logs.clear();
-  desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  // Two distinct non-Google logs.
-  desired_logs.clear();
-  desired_logs.push_back(std::string(crypto::kSHA256Length, 'A'));
-  desired_logs.push_back(std::string(crypto::kSHA256Length, 'B'));
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  // Two unique SCTs from the same non-Google log.
-  desired_logs.clear();
-  desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
-  desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
-
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  // However, there are only 4 SCTs are from distinct logs.
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
        ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
   std::unique_ptr<crypto::RSAPrivateKey> private_key(
       crypto::RSAPrivateKey::Create(1024));
@@ -477,75 +298,360 @@
   }
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest, UpdateCTLogList) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+       DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
+  SCTList scts;
+  std::vector<std::string> desired_logs;
+
+  // One Google Log.
+  desired_logs.clear();
+  desired_logs.push_back(google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  // Two distinct non-Google logs.
+  desired_logs.clear();
+  desired_logs.emplace_back(crypto::kSHA256Length, 'A');
+  desired_logs.emplace_back(crypto::kSHA256Length, 'B');
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  // Two unique SCTs from the same non-Google log.
+  desired_logs.clear();
+  desired_logs.emplace_back(crypto::kSHA256Length, 'C');
+  desired_logs.emplace_back(crypto::kSHA256Length, 'C');
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  // However, there are only 4 SCTs are from distinct logs.
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+       DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
+  SCTList scts;
+
+  // The results should be the same before and after disqualification,
+  // regardless of the delivery method.
+
+  // SCT from before disqualification.
+  scts.clear();
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                        false, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+
+  // SCT from after disqualification.
+  scts.clear();
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                        true, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+
+  // Embedded SCT from before disqualification.
+  scts.clear();
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+
+  // Embedded SCT from after disqualification.
+  scts.clear();
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+// Subclass of ChromeCTPolicyEnforcerTest that tests both with the pre-2022
+// policy and the 2022 policy.
+class ChromeCTPolicyEnforcerTestBothPolicies
+    : public ChromeCTPolicyEnforcerTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  void SetUp() override {
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kCertificateTransparency2022PolicyAllCerts);
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          /*enabled_features*/ {},
+          /*disabled_features*/ {
+              features::kCertificateTransparency2022Policy,
+              features::kCertificateTransparency2022PolicyAllCerts});
+    }
+    ChromeCTPolicyEnforcerTest::SetUp();
+  }
+};
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       ConformsToCTPolicyWithNonEmbeddedSCTs) {
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, EnforcementDisabledByBinaryAge) {
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+
+  clock_.Advance(base::Days(71));
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       ConformsToCTPolicyWithEmbeddedSCTs) {
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
+  SCTList scts;
+  std::vector<std::string> desired_logs;
+
+  // One Google log, delivered via OCSP.
+  desired_logs.clear();
+  desired_logs.push_back(google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  // One non-Google log, delivered via TLS.
+  desired_logs.clear();
+  desired_logs.push_back(non_google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           desired_logs.size(), desired_logs, true, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       ConformsToCTPolicyWithPooledEmbeddedSCTs) {
+  SCTList scts;
+  std::vector<std::string> desired_logs;
+
+  // One Google log, delivered embedded.
+  desired_logs.clear();
+  desired_logs.push_back(google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  // One non-Google log, delivered via OCSP.
+  desired_logs.clear();
+  desired_logs.push_back(non_google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
+                           desired_logs.size(), desired_logs, true, &scts);
+
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       DoesNotConformToCTPolicyNotEnoughSCTs) {
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       DoesNotConformWithDisqualifiedLogAfterDisqualificationDate) {
+  SCTList scts;
+  // Add required - 1 valid SCTs (with the old policy 5 are required, with the
+  // new policy 3).
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           GetParam() ? 2 : 4, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
+  SCTList scts;
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  // Add required - 1 valid SCTs (with the old policy 5 are required, with the
+  // new policy 3).
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           GetParam() ? 2 : 4, &scts);
+  // Make sure all SCTs are after the disqualification date.
+  for (size_t i = 1; i < scts.size(); ++i)
+    scts[i]->timestamp = scts[0]->timestamp;
+
+  if (GetParam()) {
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+    policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
+  }
+
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, UpdateCTLogList) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, &scts);
 
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          /*log_operator_history=*/{});
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  for (auto sct : scts) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator";
+    operator_history[sct->log_id] = entry;
+  }
 
-  // The check should fail since the Google Aviator log is no longer in the
-  // list after the update with an empty list.
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs, operator_history);
+
+  // The check should fail with the old policy since the Google Aviator log is
+  // no longer in the list after the update with an empty list, and with the new
+  // policy since all logs have the same operator.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            chrome_policy_enforcer->CheckCompliance(chain_.get(), scts,
-                                                    NetLogWithSource()));
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
 
   // Update the list again, this time including all the known operated by Google
-  // logs.
+  // logs, and setting operators to different values.
   operated_by_google_logs = certificate_transparency::GetLogsOperatedByGoogle();
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          /*log_operator_history=*/{});
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs, operator_history);
 
   // The check should now succeed.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            chrome_policy_enforcer->CheckCompliance(chain_.get(), scts,
-                                                    NetLogWithSource()));
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest, TimestampUpdates) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, TimestampUpdates) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
+                           1, &scts);
 
   // Clear the log list and set the last updated time to more than 10 weeks ago.
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
   std::map<std::string, OperatorHistoryEntry> log_operator_history;
-  chrome_policy_enforcer->UpdateCTLogList(
-      base::Time::Now() - base::Days(71), disqualified_logs,
-      operated_by_google_logs, log_operator_history);
+  FillOperatorHistoryWithDiverseOperators(scts, &log_operator_history);
 
-  // The check should return build not timely even though the Google Aviator log
-  // is no longer in the list, since the last update time is greater than 10
-  // weeks.
+  policy_enforcer_->UpdateCTLogList(base::Time::Now() - base::Days(71),
+                                    disqualified_logs, operated_by_google_logs,
+                                    log_operator_history);
+
+  // The check should return build not timely even though there are not enough
+  // SCTs.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY,
-            chrome_policy_enforcer->CheckCompliance(chain_.get(), scts,
-                                                    NetLogWithSource()));
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
 
   // Update the last update time value again, this time with a recent time.
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          log_operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs,
+                                    log_operator_history);
 
   // The check should now fail
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            chrome_policy_enforcer->CheckCompliance(chain_.get(), scts,
-                                                    NetLogWithSource()));
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest, IsLogDisqualifiedTimestamp) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
-
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, IsLogDisqualifiedTimestamp) {
   // Clear the log list and add 2 disqualified logs, one with a disqualification
   // date in the past and one in the future.
   // The actual logs are irrelevant for this test, so we use Aviator for the one
@@ -562,23 +668,21 @@
   disqualified_logs.emplace_back(kModifiedGoogleAviatorLogID,
                                  future_disqualification);
   disqualified_logs.emplace_back(kGoogleAviatorLogID, past_disqualification);
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          log_operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs,
+                                    log_operator_history);
 
   base::Time disqualification_time;
-  EXPECT_TRUE(chrome_policy_enforcer->IsLogDisqualified(
-      kGoogleAviatorLogID, &disqualification_time));
+  EXPECT_TRUE(policy_enforcer_->IsLogDisqualified(kGoogleAviatorLogID,
+                                                  &disqualification_time));
   EXPECT_EQ(disqualification_time, past_disqualification);
-  EXPECT_FALSE(chrome_policy_enforcer->IsLogDisqualified(
-      kModifiedGoogleAviatorLogID, &disqualification_time));
+  EXPECT_FALSE(policy_enforcer_->IsLogDisqualified(kModifiedGoogleAviatorLogID,
+                                                   &disqualification_time));
   EXPECT_EQ(disqualification_time, future_disqualification);
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest, IsLogDisqualifiedReturnsFalseOnUnknownLog) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
-
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
+       IsLogDisqualifiedReturnsFalseOnUnknownLog) {
   // Clear the log list and add a single disqualified log, with a
   // disqualification date in the past;
   const char kModifiedGoogleAviatorLogID[] =
@@ -589,27 +693,26 @@
   std::map<std::string, OperatorHistoryEntry> log_operator_history;
   disqualified_logs.emplace_back(kModifiedGoogleAviatorLogID,
                                  base::Time::Now() - base::Days(1));
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          log_operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs,
+                                    log_operator_history);
 
   base::Time unused;
   // IsLogDisqualified should return false for a log that is not in the
   // disqualified list.
   EXPECT_FALSE(
-      chrome_policy_enforcer->IsLogDisqualified(kGoogleAviatorLogID, &unused));
+      policy_enforcer_->IsLogDisqualified(kGoogleAviatorLogID, &unused));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest,
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        ConformsWithCTPolicyFutureRetirementDateLogs) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
 
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs = {google_log_id_};
   std::map<std::string, OperatorHistoryEntry> log_operator_history;
+  FillOperatorHistoryWithDiverseOperators(scts, &log_operator_history);
 
   // Set all the log operators for these SCTs as disqualified, with a timestamp
   // one hour from now.
@@ -623,9 +726,9 @@
   }
   std::sort(std::begin(disqualified_logs), std::end(disqualified_logs));
 
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          log_operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs,
+                                    log_operator_history);
 
   // SCTs should comply since retirement date is in the future.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
@@ -633,10 +736,8 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest,
+TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        DoesNotConformWithCTPolicyPastRetirementDateLogs) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
 
@@ -656,15 +757,11 @@
   }
   std::sort(std::begin(disqualified_logs), std::end(disqualified_logs));
 
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    log_operator_history[scts[i]->log_id] = entry;
-  }
+  FillOperatorHistoryWithDiverseOperators(scts, &log_operator_history);
 
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs,
-                                          log_operator_history);
+  policy_enforcer_->UpdateCTLogList(base::Time::Now(), disqualified_logs,
+                                    operated_by_google_logs,
+                                    log_operator_history);
 
   // SCTs should not comply since retirement date is in the past.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
@@ -672,6 +769,10 @@
                                               NetLogWithSource()));
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         ChromeCTPolicyEnforcerTestBothPolicies,
+                         testing::Bool());
+
 class ChromeCTPolicyEnforcerTest2022Policy : public ChromeCTPolicyEnforcerTest {
  public:
   void SetUp() override {
@@ -679,29 +780,22 @@
         features::kCertificateTransparency2022Policy);
     ChromeCTPolicyEnforcerTest::SetUp();
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
        2022PolicyNotInEffectBeforeTargetDate) {
   // Old policy should enforce one Google log requirement.
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    operator_history[scts[i]->log_id] = entry;
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+  for (auto sct : scts) {
     // Set timestamp to 1 day before new policy comes in effect.
     EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 14, 0, 0, 0, 0},
-                                            &scts[i]->timestamp));
+                                            &sct->timestamp));
   }
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -712,21 +806,17 @@
        2022PolicyInEffectAfterTargetDate) {
   // New policy should allow SCTs from all non-Google operators to comply as
   // long as diversity requirement is fulfilled.
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    operator_history[scts[i]->log_id] = entry;
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+  for (auto sct : scts) {
     // Set timestamp to the day new policy comes in effect.
     EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 15, 0, 0, 0, 0},
-                                            &scts[i]->timestamp));
+                                            &sct->timestamp));
   }
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -741,15 +831,9 @@
         features::kCertificateTransparency2022PolicyAllCerts);
     ChromeCTPolicyEnforcerTest::SetUp();
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
-
   std::unique_ptr<crypto::RSAPrivateKey> private_key(
       crypto::RSAPrivateKey::Create(1024));
   ASSERT_TRUE(private_key);
@@ -802,12 +886,8 @@
                                std::vector<std::string>(), false, &scts);
       // Add different operators to the logs so the SCTs comply with operator
       // diversity.
-      for (size_t k = 0; k < scts.size(); k++) {
-        OperatorHistoryEntry entry;
-        entry.current_operator_ = "Operator " + base::NumberToString(k);
-        operator_history[scts[k]->log_id] = entry;
-      }
-      chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+      FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+      policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
       CTPolicyCompliance expected;
       if (j == scts_required) {
         // If the scts provided are as many as are required, the cert should be
@@ -830,18 +910,16 @@
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
        DoesNotConformToCTPolicyAllLogsSameOperator) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
-  for (size_t i = 0; i < scts.size(); i++) {
+  for (auto sct : scts) {
     OperatorHistoryEntry entry;
     entry.current_operator_ = "Operator";
-    operator_history[scts[i]->log_id] = entry;
+    operator_history[sct->log_id] = entry;
   }
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -850,18 +928,12 @@
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
        ConformsToCTPolicyDifferentOperators) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    operator_history[scts[i]->log_id] = entry;
-  }
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -870,8 +942,6 @@
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
        ConformsToPolicyDueToOperatorSwitch) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -886,7 +956,7 @@
   // end time after the SCT timestamp.
   operator_history[scts[1]->log_id].previous_operators_.emplace_back(
       "Different Operator", scts[1]->timestamp + base::Seconds(1));
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -895,23 +965,18 @@
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
        DoesNotConformToPolicyDueToOperatorSwitch) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
   // Set logs to different operators
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    operator_history[scts[i]->log_id] = entry;
-  }
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
+
   // Set the previous operator of one of the logs to the same as the other log,
   // with an end time after the SCT timestamp.
   operator_history[scts[1]->log_id].previous_operators_.emplace_back(
       "Operator 0", scts[1]->timestamp + base::Seconds(1));
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -919,25 +984,19 @@
 }
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, MultipleOperatorSwitches) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
   std::map<std::string, OperatorHistoryEntry> operator_history;
   // Set logs to different operators
-  for (size_t i = 0; i < scts.size(); i++) {
-    OperatorHistoryEntry entry;
-    entry.current_operator_ = "Operator " + base::NumberToString(i);
-    operator_history[scts[i]->log_id] = entry;
-  }
+  FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
   // Set multiple previous operators, the first should be ignored since it
   // stopped operating before the SCT timestamp.
   operator_history[scts[1]->log_id].previous_operators_.emplace_back(
       "Different Operator", scts[1]->timestamp - base::Seconds(1));
   operator_history[scts[1]->log_id].previous_operators_.emplace_back(
       "Operator 0", scts[1]->timestamp + base::Seconds(1));
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
@@ -946,8 +1005,6 @@
 
 TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
        MultipleOperatorSwitchesBeforeSCTTimestamp) {
-  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
-      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -964,7 +1021,7 @@
       "Different Operator", scts[1]->timestamp - base::Seconds(2));
   operator_history[scts[1]->log_id].previous_operators_.emplace_back(
       "Yet Another Different Operator", scts[1]->timestamp - base::Seconds(1));
-  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+  policy_enforcer_->SetOperatorHistoryForTesting(operator_history);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
diff --git a/components/media_message_center/media_controls_progress_view_unittest.cc b/components/media_message_center/media_controls_progress_view_unittest.cc
index 6c350e3..62ba7a0 100644
--- a/components/media_message_center/media_controls_progress_view_unittest.cc
+++ b/components/media_message_center/media_controls_progress_view_unittest.cc
@@ -126,7 +126,9 @@
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .55);
 }
 
-TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressFastPlayback) {
+// Flaky on multiple platforms. crbug.com/1293864
+TEST_F(MAYBE_MediaControlsProgressViewTest,
+       DISABLED_UpdateProgressFastPlayback) {
   media_session::MediaPosition media_position(
       /*playback_rate=*/2, /*duration=*/base::Seconds(600),
       /*position=*/base::Seconds(300), /*end_of_media=*/false);
diff --git a/components/metrics/content/content_stability_metrics_provider.cc b/components/metrics/content/content_stability_metrics_provider.cc
index 790402d..092ce17 100644
--- a/components/metrics/content/content_stability_metrics_provider.cc
+++ b/components/metrics/content/content_stability_metrics_provider.cc
@@ -22,16 +22,15 @@
 #endif
 
 namespace metrics {
-
 namespace {
 
+using content::RenderProcessHost;
+
 base::LazyInstance<std::vector<ContentStabilityMetricsProvider*>>::Leaky
     g_providers;
 
 }  // namespace
 
-using content::RenderProcessHost;
-
 ContentStabilityMetricsProvider::ContentStabilityMetricsProvider(
     PrefService* local_state,
     std::unique_ptr<ExtensionsHelper> extensions_helper)
@@ -75,18 +74,8 @@
     const content::ChildProcessData& data,
     const content::ChildProcessTerminationInfo& info) {
   DCHECK(!data.metrics_name.empty());
-#if BUILDFLAG(ENABLE_PLUGINS)
-  // Exclude plugin crashes from the count below because we report them via
-  // a separate UMA metric.
-  if (data.process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
-      data.process_type == content::PROCESS_TYPE_PPAPI_BROKER) {
-    return;
-  }
-#endif
-
   if (data.process_type == content::PROCESS_TYPE_UTILITY)
     helper_.BrowserUtilityProcessCrashed(data.metrics_name, info.exit_code);
-  helper_.BrowserChildProcessCrashed();
 }
 
 void ContentStabilityMetricsProvider::BrowserChildProcessLaunchedAndConnected(
diff --git a/components/metrics/content/content_stability_metrics_provider_unittest.cc b/components/metrics/content/content_stability_metrics_provider_unittest.cc
index 36a0901..3d27fc4 100644
--- a/components/metrics/content/content_stability_metrics_provider_unittest.cc
+++ b/components/metrics/content/content_stability_metrics_provider_unittest.cc
@@ -31,7 +31,6 @@
 
 namespace {
 
-const char kTestGpuProcessName[] = "content_gpu";
 const char kTestUtilityProcessName[] = "test_utility_process";
 
 class MockExtensionsHelper : public ExtensionsHelper {
@@ -73,38 +72,6 @@
   content::BrowserTaskEnvironment task_environment_;
 };
 
-TEST_F(ContentStabilityMetricsProviderTest, BrowserChildProcessObserverGpu) {
-  base::HistogramTester histogram_tester;
-  metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
-
-  content::ChildProcessData child_process_data(content::PROCESS_TYPE_GPU);
-  child_process_data.metrics_name = kTestGpuProcessName;
-
-  provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
-  content::ChildProcessTerminationInfo abnormal_termination_info;
-  abnormal_termination_info.status =
-      base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
-  abnormal_termination_info.exit_code = 1;
-  provider.BrowserChildProcessCrashed(child_process_data,
-                                      abnormal_termination_info);
-  provider.BrowserChildProcessCrashed(child_process_data,
-                                      abnormal_termination_info);
-
-  // Call ProvideStabilityMetrics to check that it will force pending tasks to
-  // be executed immediately.
-  metrics::SystemProfileProto system_profile;
-
-  provider.ProvideStabilityMetrics(&system_profile);
-
-  // Check current number of instances created.
-  const metrics::SystemProfileProto_Stability& stability =
-      system_profile.stability();
-
-  EXPECT_EQ(2, stability.child_process_crash_count());
-  EXPECT_TRUE(
-      histogram_tester.GetTotalCountsForPrefix("ChildProcess.").empty());
-}
-
 TEST_F(ContentStabilityMetricsProviderTest,
        BrowserChildProcessObserverUtility) {
   base::HistogramTester histogram_tester;
@@ -124,20 +91,7 @@
   provider.BrowserChildProcessCrashed(child_process_data,
                                       abnormal_termination_info);
 
-  // Call ProvideStabilityMetrics to check that it will force pending tasks to
-  // be executed immediately.
-  metrics::SystemProfileProto system_profile;
-
-  provider.ProvideStabilityMetrics(&system_profile);
-
-  // Check current number of instances created.
-  const metrics::SystemProfileProto_Stability& stability =
-      system_profile.stability();
-
-  EXPECT_EQ(2, stability.child_process_crash_count());
-
-  // Utility processes also log an entries for the hashed name of the process
-  // for launches and crashes.
+  // Verify metrics.
   histogram_tester.ExpectUniqueSample(
       "ChildProcess.Launched.UtilityProcessHash",
       variations::HashName(kTestUtilityProcessName), 1);
@@ -146,6 +100,8 @@
       variations::HashName(kTestUtilityProcessName), 2);
   histogram_tester.ExpectUniqueSample(
       "ChildProcess.Crashed.UtilityProcessExitCode", kExitCode, 2);
+  histogram_tester.ExpectUniqueSample("Stability.Counts2",
+                                      StabilityEventType::kUtilityCrash, 2);
 }
 
 TEST_F(ContentStabilityMetricsProviderTest, NotificationObserver) {
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
index c222250..ca3b012 100644
--- a/components/metrics/metrics_pref_names.cc
+++ b/components/metrics/metrics_pref_names.cc
@@ -109,12 +109,6 @@
 const char kStabilityBrowserLastLiveTimeStamp[] =
     "user_experience_metrics.stability.browser_last_live_timestamp";
 
-// Total number of child process crashes (other than renderer / extension
-// renderer ones, and plugin children, which are counted separately) since the
-// last report.
-const char kStabilityChildProcessCrashCount[] =
-    "user_experience_metrics.stability.child_process_crash_count";
-
 // Number of times the application exited uncleanly since the last report.
 // On Android this does not count the ones due to Gms Core updates (below).
 const char kStabilityCrashCount[] =
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
index 91e9c8b..59c57eb 100644
--- a/components/metrics/metrics_pref_names.h
+++ b/components/metrics/metrics_pref_names.h
@@ -41,7 +41,6 @@
 
 // Preferences for recording stability logs.
 extern const char kStabilityBrowserLastLiveTimeStamp[];
-extern const char kStabilityChildProcessCrashCount[];
 extern const char kStabilityCrashCount[];
 extern const char kStabilityCrashCountDueToGmsCoreUpdate[];
 extern const char kStabilityExitedCleanly[];
diff --git a/components/metrics/stability_metrics_helper.cc b/components/metrics/stability_metrics_helper.cc
index 83f8b7f..47b48818 100644
--- a/components/metrics/stability_metrics_helper.cc
+++ b/components/metrics/stability_metrics_helper.cc
@@ -85,12 +85,6 @@
     local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0);
   }
 
-  count = local_state_->GetInteger(prefs::kStabilityChildProcessCrashCount);
-  if (count) {
-    stability_proto->set_child_process_crash_count(count);
-    local_state_->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
-  }
-
   count = local_state_->GetInteger(prefs::kStabilityGpuCrashCount);
   if (count) {
     stability_proto->set_gpu_crash_count(count);
@@ -140,7 +134,6 @@
 
 void StabilityMetricsHelper::ClearSavedStabilityMetrics() {
   // Clear all the prefs used in this class in UMA reports.
-  local_state_->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
   local_state_->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0);
   local_state_->SetInteger(prefs::kStabilityExtensionRendererFailedLaunchCount,
                            0);
@@ -154,7 +147,6 @@
 
 // static
 void StabilityMetricsHelper::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
   registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount,
                                 0);
   registry->RegisterIntegerPref(
@@ -215,11 +207,6 @@
   // trigger a Stability Event.
 }
 
-void StabilityMetricsHelper::BrowserChildProcessCrashed() {
-  IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
-  RecordStabilityEvent(StabilityEventType::kChildProcessCrash);
-}
-
 void StabilityMetricsHelper::LogLoadStarted() {
   IncrementPrefValue(prefs::kStabilityPageLoadCount);
   RecordStabilityEvent(StabilityEventType::kPageLoad);
diff --git a/components/metrics/stability_metrics_helper.h b/components/metrics/stability_metrics_helper.h
index af25e9f..f4069fd 100644
--- a/components/metrics/stability_metrics_helper.h
+++ b/components/metrics/stability_metrics_helper.h
@@ -29,7 +29,7 @@
   kRendererCrash = 3,
   kRendererHang = 4,
   kExtensionCrash = 5,
-  kChildProcessCrash = 6,
+  // kChildProcessCrash = 6,  // Removed due to disuse and alternative metrics.
   kLaunch = 15,
   kBrowserCrash = 16,
   // kIncompleteShutdown = 17,  // Removed due to disuse and correctness issues.
@@ -80,9 +80,6 @@
 #endif
   );
 
-  // Records a browser child process crash.
-  void BrowserChildProcessCrashed();
-
   // Logs the initiation of a page load.
   void LogLoadStarted();
 
diff --git a/components/metrics/stability_metrics_helper_unittest.cc b/components/metrics/stability_metrics_helper_unittest.cc
index ea11527..dc3205b3 100644
--- a/components/metrics/stability_metrics_helper_unittest.cc
+++ b/components/metrics/stability_metrics_helper_unittest.cc
@@ -47,28 +47,6 @@
 
 }  // namespace
 
-TEST_F(StabilityMetricsHelperTest, BrowserChildProcessCrashed) {
-  StabilityMetricsHelper helper(prefs());
-  base::HistogramTester histogram_tester;
-
-  helper.BrowserChildProcessCrashed();
-  helper.BrowserChildProcessCrashed();
-
-  // Call ProvideStabilityMetrics to check that it will force pending tasks to
-  // be executed immediately.
-  metrics::SystemProfileProto system_profile;
-
-  helper.ProvideStabilityMetrics(&system_profile);
-
-  // Check current number of instances created.
-  const metrics::SystemProfileProto_Stability& stability =
-      system_profile.stability();
-
-  EXPECT_EQ(2, stability.child_process_crash_count());
-  histogram_tester.ExpectUniqueSample(
-      "Stability.Counts2", StabilityEventType::kChildProcessCrash, 2);
-}
-
 TEST_F(StabilityMetricsHelperTest, LogRendererCrash) {
   StabilityMetricsHelper helper(prefs());
   base::HistogramTester histogram_tester;
diff --git a/components/optimization_guide/core/page_entities_model_executor_impl.cc b/components/optimization_guide/core/page_entities_model_executor_impl.cc
index 3d89bdc..78726d5a 100644
--- a/components/optimization_guide/core/page_entities_model_executor_impl.cc
+++ b/components/optimization_guide/core/page_entities_model_executor_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/optimization_guide/core/page_entities_model_executor_impl.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/optimization_guide/core/entity_annotator_native_library.h"
 #include "components/optimization_guide/core/optimization_guide_model_provider.h"
@@ -95,8 +96,15 @@
   absl::optional<std::vector<ScoredEntityMetadata>> scored_md;
   if (entity_annotator_) {
     DCHECK(entity_annotator_native_library_);
+    base::TimeTicks start_time = base::TimeTicks::Now();
     scored_md =
         entity_annotator_native_library_->AnnotateText(entity_annotator_, text);
+    // The max of this histogram is 1 hour because we want to understand
+    // tail behavior and catch long running model executions.
+    base::UmaHistogramLongTimes(
+        "OptimizationGuide.PageContentAnnotationsService.ModelExecutionLatency."
+        "PageEntities",
+        base::TimeTicks::Now() - start_time);
   }
   reply_task_runner_->PostTask(FROM_HERE,
                                base::BindOnce(std::move(callback), scored_md));
diff --git a/components/pdf/renderer/internal_plugin_renderer_helpers.cc b/components/pdf/renderer/internal_plugin_renderer_helpers.cc
index dce1a14..3334ea45 100644
--- a/components/pdf/renderer/internal_plugin_renderer_helpers.cc
+++ b/components/pdf/renderer/internal_plugin_renderer_helpers.cc
@@ -64,6 +64,9 @@
     return nullptr;
   }
 
+  // Only create the in-process plugin within a PDF renderer.
+  CHECK(IsPdfRenderer());
+
   // Origins allowed to embed the internal plugin are trusted (the PDF viewer
   // and Print Preview), and should never directly create the in-process plugin.
   // Likewise, they should not share a process with this frame.
diff --git a/components/reporting/util/disconnectable_client.cc b/components/reporting/util/disconnectable_client.cc
index 687b636d..76d11b72 100644
--- a/components/reporting/util/disconnectable_client.cc
+++ b/components/reporting/util/disconnectable_client.cc
@@ -5,6 +5,7 @@
 #include "components/reporting/util/disconnectable_client.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/containers/fixed_flat_map.h"
@@ -34,16 +35,13 @@
         Status(reporting::error::UNAVAILABLE, "Service is unavailable"));
     return;
   }
-  // Save raw pointer to the |delegate|. |delegate| cannot be released until
-  // we leave this method (we must get on |task_runner_| again first).
-  auto* const raw_delegate_ptr = delegate.get();
   // Add the delegate to the map.
   const auto id = base::RandUint64();
   auto res = outstanding_delegates_.emplace(id, std::move(delegate));
   DCHECK(res.second) << "Duplicate call id " << id;
   // Make a call, resume on CallResponded, when response is received.
-  raw_delegate_ptr->DoCall(base::BindOnce(&DisconnectableClient::CallResponded,
-                                          weak_ptr_factory_.GetWeakPtr(), id));
+  res.first->second->DoCall(base::BindOnce(&DisconnectableClient::CallResponded,
+                                           weak_ptr_factory_.GetWeakPtr(), id));
 }
 
 void DisconnectableClient::CallResponded(uint64_t id) {
@@ -67,11 +65,11 @@
                << "available";
   if (!is_available_) {
     // Cancel all pending calls.
-    auto delegates = std::move(outstanding_delegates_);
-    for (auto& p : delegates) {
+    for (auto& p : outstanding_delegates_) {
       std::move(p.second)->Respond(
           Status(reporting::error::UNAVAILABLE, "Service is unavailable"));
     }
+    outstanding_delegates_.clear();
   }
 }
 
diff --git a/components/reporting/util/disconnectable_client_unittest.cc b/components/reporting/util/disconnectable_client_unittest.cc
index 23c816d..36f653a 100644
--- a/components/reporting/util/disconnectable_client_unittest.cc
+++ b/components/reporting/util/disconnectable_client_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/reporting/util/disconnectable_client.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/test/task_environment.h"
 #include "base/threading/sequenced_task_runner_handle.h"
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 561f06c..24aee9c 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -363,16 +363,19 @@
   // viewport size is never set.
   bool use_stencil = overdraw_feedback_;
   bool needs_full_frame_redraw = false;
+  auto display_transform = output_surface_->GetDisplayTransform();
   if (surface_resource_size != reshape_surface_size_ ||
       device_scale_factor != reshape_device_scale_factor_ ||
       frame_color_space != reshape_color_space_ ||
       frame_buffer_format != reshape_buffer_format_ ||
-      use_stencil != reshape_use_stencil_) {
+      use_stencil != reshape_use_stencil_ ||
+      display_transform != reshape_display_transform_) {
     reshape_surface_size_ = surface_resource_size;
     reshape_device_scale_factor_ = device_scale_factor;
     reshape_color_space_ = frame_color_space;
     reshape_buffer_format_ = frame_buffer_format;
     reshape_use_stencil_ = overdraw_feedback_;
+    reshape_display_transform_ = display_transform;
     output_surface_->Reshape(reshape_surface_size_,
                              reshape_device_scale_factor_, reshape_color_space_,
                              *reshape_buffer_format_, reshape_use_stencil_);
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index 5524d69..a9ec5f4 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -385,6 +385,8 @@
   gfx::ColorSpace reshape_color_space_;
   absl::optional<gfx::BufferFormat> reshape_buffer_format_;
   bool reshape_use_stencil_ = false;
+  gfx::OverlayTransform reshape_display_transform_ =
+      gfx::OVERLAY_TRANSFORM_INVALID;
 };
 
 }  // namespace viz
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e2bbf2e88..dc1b872f 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -116,6 +116,7 @@
     "//content/browser/payments:payment_app_proto",
     "//content/browser/prerender:mojo_bindings",
     "//content/browser/process_internals:mojo_bindings",
+    "//content/browser/resources:resources",
     "//content/browser/resources/media:resources",
     "//content/browser/webrtc/resources",
     "//content/common",
diff --git a/content/browser/aggregation_service/aggregatable_report.cc b/content/browser/aggregation_service/aggregatable_report.cc
index 8fdb917..807e769 100644
--- a/content/browser/aggregation_service/aggregatable_report.cc
+++ b/content/browser/aggregation_service/aggregatable_report.cc
@@ -16,6 +16,7 @@
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/containers/span.h"
+#include "base/json/json_string_value_serializer.h"
 #include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
@@ -39,14 +40,6 @@
 using DpfKey = distributed_point_functions::DpfKey;
 using DpfParameters = distributed_point_functions::DpfParameters;
 
-// Shared info:
-constexpr char kPrivacyBudgetKeyKey[] = "privacy_budget_key";
-constexpr char kScheduledReportTimeKey[] = "scheduled_report_time";
-constexpr char kVersionKey[] = "version";
-
-// TODO(alexmt): Replace with a real version once a version string is decided.
-constexpr char kVersionValue[] = "";
-
 // Payload contents:
 constexpr char kHistogramValue[] = "histogram";
 constexpr char kOperationKey[] = "operation";
@@ -103,31 +96,11 @@
   return dpf_keys;
 }
 
-// Helper that encodes the shared information to a CBOR map. Non-shared
-// information should then be added as appropriate.
-cbor::Value::MapValue EncodeSharedInfoToCbor(
-    const AggregatableReportSharedInfo& shared_info) {
-  cbor::Value::MapValue value;
-
-  value.emplace(kPrivacyBudgetKeyKey, shared_info.privacy_budget_key);
-
-  // Encoded as the number of milliseconds since the Unix epoch, ignoring leap
-  // seconds.
-  DCHECK(!shared_info.scheduled_report_time.is_null());
-  DCHECK(!shared_info.scheduled_report_time.is_inf());
-  value.emplace(kScheduledReportTimeKey,
-                shared_info.scheduled_report_time.ToJavaTime());
-  value.emplace(kVersionKey, kVersionValue);
-
-  return value;
-}
-
 // Returns a vector with a serialized CBOR map for each processing origin. See
 // the AggregatableReport documentation for more detail on the expected format.
 // Returns an empty vector in case of error.
 std::vector<std::vector<uint8_t>> ConstructUnencryptedTwoPartyPayloads(
-    const AggregationServicePayloadContents& payload_contents,
-    const AggregatableReportSharedInfo& shared_info) {
+    const AggregationServicePayloadContents& payload_contents) {
   std::vector<DpfKey> dpf_keys = GenerateDpfKeys(payload_contents);
   if (dpf_keys.empty()) {
     return {};
@@ -141,9 +114,7 @@
         dpf_key.SerializeToArray(serialized_key.data(), serialized_key.size());
     DCHECK(succeeded);
 
-    // Start with putting all shared info in the unencrypted payload.
-    cbor::Value::MapValue value = EncodeSharedInfoToCbor(shared_info);
-
+    cbor::Value::MapValue value;
     value.emplace(kReportingOriginKey,
                   payload_contents.reporting_origin.Serialize());
     value.emplace(kOperationKey, kHistogramValue);
@@ -167,16 +138,14 @@
 // Returns an empty vector in case of error.
 std::vector<std::vector<uint8_t>> ConstructUnencryptedSingleServerPayloads(
     const AggregationServicePayloadContents& payload_contents,
-    const AggregatableReportSharedInfo& shared_info,
     size_t num_processing_origins) {
   std::vector<std::vector<uint8_t>> unencrypted_payloads;
+
   // If multiple processing origins are specified, one is randomly picked to
   // encode the actual data.
   size_t index_to_populate = base::RandGenerator(num_processing_origins);
   for (size_t i = 0; i < num_processing_origins; ++i) {
-    // Start with putting all shared info in the unencrypted payload.
-    cbor::Value::MapValue value = EncodeSharedInfoToCbor(shared_info);
-
+    cbor::Value::MapValue value;
     value.emplace(kReportingOriginKey,
                   payload_contents.reporting_origin.Serialize());
     value.emplace(kOperationKey, kHistogramValue);
@@ -274,6 +243,30 @@
     : scheduled_report_time(std::move(scheduled_report_time)),
       privacy_budget_key(std::move(privacy_budget_key)) {}
 
+std::string AggregatableReportSharedInfo::SerializeAsJson() const {
+  base::Value value(base::Value::Type::DICTIONARY);
+
+  value.SetStringKey("privacy_budget_key", privacy_budget_key);
+
+  // TODO(crbug.com/1251648): Update timestamp to use seconds.
+  // Encoded as the number of milliseconds since the Unix epoch, ignoring leap
+  // seconds.
+  DCHECK(!scheduled_report_time.is_null());
+  DCHECK(!scheduled_report_time.is_inf());
+  value.SetStringKey("scheduled_report_time",
+                     base::NumberToString(scheduled_report_time.ToJavaTime()));
+
+  // TODO(alexmt): Replace with a real version once a version string is decided.
+  value.SetStringKey("version", "");
+
+  std::string serialized_value;
+  JSONStringValueSerializer serializer(&serialized_value);
+  bool succeeded = serializer.Serialize(value);
+  DCHECK(succeeded);
+
+  return serialized_value;
+}
+
 // static
 absl::optional<AggregatableReportRequest> AggregatableReportRequest::Create(
     std::vector<url::Origin> processing_origins,
@@ -341,7 +334,7 @@
 
 AggregatableReport::AggregatableReport(
     std::vector<AggregationServicePayload> payloads,
-    AggregatableReportSharedInfo shared_info)
+    std::string shared_info)
     : payloads_(std::move(payloads)), shared_info_(std::move(shared_info)) {}
 
 AggregatableReport::AggregatableReport(const AggregatableReport& other) =
@@ -359,7 +352,7 @@
 
 constexpr size_t AggregatableReport::kBucketDomainBitLength;
 constexpr size_t AggregatableReport::kValueDomainBitLength;
-constexpr char AggregatableReport::kDomainSeparationValue[];
+constexpr char AggregatableReport::kDomainSeparationPrefix[];
 
 // static
 bool AggregatableReport::Provider::g_disable_encryption_for_testing_tool_ =
@@ -389,13 +382,12 @@
   switch (report_request.payload_contents().processing_type) {
     case AggregationServicePayloadContents::ProcessingType::kTwoParty: {
       unencrypted_payloads = ConstructUnencryptedTwoPartyPayloads(
-          report_request.payload_contents(), report_request.shared_info());
+          report_request.payload_contents());
       break;
     }
     case AggregationServicePayloadContents::ProcessingType::kSingleServer: {
       unencrypted_payloads = ConstructUnencryptedSingleServerPayloads(
-          report_request.payload_contents(), report_request.shared_info(),
-          num_processing_origins);
+          report_request.payload_contents(), num_processing_origins);
       break;
     }
   }
@@ -404,11 +396,15 @@
     return absl::nullopt;
   }
 
-  // TODO(crbug.com/1251648): Resolve whether to use AEAD to ensure shared info
-  // is identical for reporting and aggregation origins.
   std::vector<uint8_t> authenticated_info(
-      kDomainSeparationValue,
-      kDomainSeparationValue + sizeof(kDomainSeparationValue));
+      kDomainSeparationPrefix,
+      kDomainSeparationPrefix + sizeof(kDomainSeparationPrefix));
+
+  std::string encoded_shared_info =
+      report_request.shared_info_.SerializeAsJson();
+  authenticated_info.insert(authenticated_info.end(),
+                            encoded_shared_info.begin(),
+                            encoded_shared_info.end());
 
   // To avoid unnecessary copies, we move the processing origins and shared info
   // from the `report_request`'s private members. Note that the request object
@@ -433,22 +429,13 @@
   }
 
   return AggregatableReport(std::move(encrypted_payloads),
-                            std::move(report_request.shared_info_));
+                            std::move(encoded_shared_info));
 }
 
 base::Value::DictStorage AggregatableReport::GetAsJson() const {
   base::Value::DictStorage value;
 
-  value.emplace(kPrivacyBudgetKeyKey, shared_info_.privacy_budget_key);
-
-  // Encoded as a string representing the number of milliseconds since the Unix
-  // epoch, ignoring leap seconds.
-  DCHECK(!shared_info_.scheduled_report_time.is_null());
-  DCHECK(!shared_info_.scheduled_report_time.is_inf());
-  value.emplace(
-      kScheduledReportTimeKey,
-      base::NumberToString(shared_info_.scheduled_report_time.ToJavaTime()));
-  value.emplace(kVersionKey, kVersionValue);
+  value.emplace("shared_info", shared_info_);
 
   base::Value payloads_list_value(base::Value::Type::LIST);
   for (const AggregationServicePayload& payload : payloads_) {
diff --git a/content/browser/aggregation_service/aggregatable_report.h b/content/browser/aggregation_service/aggregatable_report.h
index fe6d0db..54aadd13 100644
--- a/content/browser/aggregation_service/aggregatable_report.h
+++ b/content/browser/aggregation_service/aggregatable_report.h
@@ -59,6 +59,10 @@
 struct CONTENT_EXPORT AggregatableReportSharedInfo {
   AggregatableReportSharedInfo(base::Time scheduled_report_time,
                                std::string privacy_budget_key);
+
+  // Serializes to a JSON dictionary, represented as a string.
+  std::string SerializeAsJson() const;
+
   base::Time scheduled_report_time;
   std::string privacy_budget_key;
 };
@@ -134,15 +138,15 @@
   // log_2 of the value output space
   static constexpr size_t kValueDomainBitLength = 64;
 
-  // Used as the authenticated information (i.e. context info). This value must
-  // not be reused for new protocols or versions of this protocol unless the
-  // ciphertexts are intended to be compatible. This ensures that, even if
-  // public keys are reused, the same ciphertext cannot be (i.e. no cross-
-  // protocol attacks).
-  static constexpr char kDomainSeparationValue[] = "aggregation_service";
+  // Used as a prefix for the authenticated information (i.e. context info).
+  // This value must not be reused for new protocols or versions of this
+  // protocol unless the ciphertexts are intended to be compatible. This ensures
+  // that, even if public keys are reused, the same ciphertext cannot be (i.e.
+  // no cross-protocol attacks).
+  static constexpr char kDomainSeparationPrefix[] = "aggregation_service";
 
   AggregatableReport(std::vector<AggregationServicePayload> payloads,
-                     AggregatableReportSharedInfo shared_info);
+                     std::string shared_info);
   AggregatableReport(const AggregatableReport& other);
   AggregatableReport& operator=(const AggregatableReport& other);
   AggregatableReport(AggregatableReport&& other);
@@ -152,15 +156,14 @@
   const std::vector<AggregationServicePayload>& payloads() const {
     return payloads_;
   }
-  const AggregatableReportSharedInfo& shared_info() const {
-    return shared_info_;
-  }
+  const std::string& shared_info() const { return shared_info_; }
 
+  // TODO(crbug.com/1251648): Update timestamp to use seconds.
   // Returns the JSON representation of this report of the form
   // {
-  //   "scheduled_report_time": "<timestamp in msec>",
-  //   "privacy_budget_key": "<field for server to do privacy budgeting>",
-  //   "version": "<api version>",
+  //   "shared_info": "{\"scheduled_report_time\":\"[timestamp in
+  //   msec]\",\"privacy_budget_key\":\"[string]\",\"version\":\"[api
+  //   version]\"}",
   //   "aggregation_service_payloads": [
   //     {
   //       "origin": "https://helper1.example",
@@ -192,7 +195,7 @@
   // the original AggregatableReportRequest.
   std::vector<AggregationServicePayload> payloads_;
 
-  AggregatableReportSharedInfo shared_info_;
+  std::string shared_info_;
 };
 
 // Represents a request for an AggregatableReport. Contains all the data
diff --git a/content/browser/aggregation_service/aggregatable_report_unittest.cc b/content/browser/aggregation_service/aggregatable_report_unittest.cc
index 163325e..1d20b14 100644
--- a/content/browser/aggregation_service/aggregatable_report_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_unittest.cc
@@ -26,15 +26,20 @@
 
 namespace content {
 
-std::vector<uint8_t> DecryptPayloadWithHpke(const std::vector<uint8_t>& payload,
-                                            const EVP_HPKE_KEY& key) {
+std::vector<uint8_t> DecryptPayloadWithHpke(
+    const std::vector<uint8_t>& payload,
+    const EVP_HPKE_KEY& key,
+    const std::string& expected_serialized_shared_info) {
   base::span<const uint8_t> enc =
       base::make_span(payload).subspan(0, X25519_PUBLIC_VALUE_LEN);
 
   std::vector<uint8_t> authenticated_info(
-      AggregatableReport::kDomainSeparationValue,
-      AggregatableReport::kDomainSeparationValue +
-          sizeof(AggregatableReport::kDomainSeparationValue));
+      AggregatableReport::kDomainSeparationPrefix,
+      AggregatableReport::kDomainSeparationPrefix +
+          sizeof(AggregatableReport::kDomainSeparationPrefix));
+  authenticated_info.insert(authenticated_info.end(),
+                            expected_serialized_shared_info.begin(),
+                            expected_serialized_shared_info.end());
 
   bssl::ScopedEVP_HPKE_CTX recipient_context;
   if (!EVP_HPKE_CTX_setup_recipient(
@@ -94,8 +99,9 @@
     const std::vector<aggregation_service::TestHpkeKey>& encryption_keys) {
   ASSERT_TRUE(report.has_value());
 
-  EXPECT_TRUE(aggregation_service::SharedInfoEqual(report->shared_info(),
-                                                   expected_shared_info));
+  std::string expected_serialized_shared_info =
+      expected_shared_info.SerializeAsJson();
+  EXPECT_EQ(report->shared_info(), expected_serialized_shared_info);
 
   const std::vector<AggregatableReport::AggregationServicePayload>& payloads =
       report->payloads();
@@ -110,7 +116,8 @@
     EXPECT_EQ(payloads[i].key_id, encryption_keys[i].public_key.id);
 
     std::vector<uint8_t> decrypted_payload = DecryptPayloadWithHpke(
-        payloads[i].payload, encryption_keys[i].full_hpke_key);
+        payloads[i].payload, encryption_keys[i].full_hpke_key,
+        expected_serialized_shared_info);
     ASSERT_FALSE(decrypted_payload.empty());
 
     absl::optional<cbor::Value> deserialized_payload =
@@ -119,11 +126,7 @@
     ASSERT_TRUE(deserialized_payload->is_map());
     const cbor::Value::MapValue& payload_map = deserialized_payload->GetMap();
 
-    EXPECT_EQ(payload_map.size(), 6UL);
-
-    ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "version",
-                                          cbor::Value::Type::STRING));
-    EXPECT_EQ(payload_map.at(cbor::Value("version")).GetString(), "");
+    EXPECT_EQ(payload_map.size(), 3UL);
 
     ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "reporting_origin",
                                           cbor::Value::Type::STRING));
@@ -131,16 +134,6 @@
                   payload_map.at(cbor::Value("reporting_origin")).GetString())),
               expected_payload_contents.reporting_origin);
 
-    ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "privacy_budget_key",
-                                          cbor::Value::Type::STRING));
-    EXPECT_EQ(payload_map.at(cbor::Value("privacy_budget_key")).GetString(),
-              expected_shared_info.privacy_budget_key);
-
-    ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "scheduled_report_time",
-                                          cbor::Value::Type::UNSIGNED));
-    EXPECT_EQ(payload_map.at(cbor::Value("scheduled_report_time")).GetInteger(),
-              expected_shared_info.scheduled_report_time.ToJavaTime());
-
     ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "operation",
                                           cbor::Value::Type::STRING));
     EXPECT_EQ(payload_map.at(cbor::Value("operation")).GetString(),
@@ -479,11 +472,7 @@
                         /*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1");
 
-  AggregatableReportSharedInfo shared_info(
-      base::Time::FromJavaTime(1234567890123),
-      /*privacy_budget_key=*/"example_pbk");
-
-  AggregatableReport report(std::move(payloads), std::move(shared_info));
+  AggregatableReport report(std::move(payloads), "example_shared_info");
   base::Value::DictStorage report_json_value = report.GetAsJson();
 
   std::string report_json_string;
@@ -494,8 +483,7 @@
       R"("aggregation_service_payloads":[)"
       R"({"key_id":"key_1","origin":"https://a.example","payload":"ABCD1234"})"
       R"(],)"
-      R"("privacy_budget_key":"example_pbk",)"
-      R"("scheduled_report_time":"1234567890123","version":"")"
+      R"("shared_info":"example_shared_info")"
       R"(})";
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
@@ -509,11 +497,7 @@
                         /*payload=*/kEFGH5678AsBytes,
                         /*key_id=*/"key_2");
 
-  AggregatableReportSharedInfo shared_info(
-      base::Time::FromJavaTime(1234567890123),
-      /*privacy_budget_key=*/"example_pbk");
-
-  AggregatableReport report(std::move(payloads), std::move(shared_info));
+  AggregatableReport report(std::move(payloads), "example_shared_info");
   base::Value::DictStorage report_json_value = report.GetAsJson();
 
   std::string report_json_string;
@@ -525,10 +509,23 @@
       R"({"key_id":"key_1","origin":"https://a.example","payload":"ABCD1234"},)"
       R"({"key_id":"key_2","origin":"https://b.example","payload":"EFGH5678"})"
       R"(],)"
-      R"("privacy_budget_key":"example_pbk",)"
-      R"("scheduled_report_time":"1234567890123","version":"")"
+      R"("shared_info":"example_shared_info")"
       R"(})";
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
+TEST(AggregatableReportTest, SharedInfoSerializeAsJson_ReturnsExpectedString) {
+  AggregatableReportSharedInfo shared_info(
+      base::Time::FromJavaTime(1234567890123),
+      /*privacy_budget_key=*/"example_pbk");
+
+  const char kExpectedString[] = R"({)"
+                                 R"("privacy_budget_key":"example_pbk",)"
+                                 R"("scheduled_report_time":"1234567890123",)"
+                                 R"("version":"")"
+                                 R"(})";
+
+  EXPECT_EQ(shared_info.SerializeAsJson(), kExpectedString);
+}
+
 }  // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_impl_unittest.cc b/content/browser/aggregation_service/aggregation_service_impl_unittest.cc
index e1d83c5b..fbee987 100644
--- a/content/browser/aggregation_service/aggregation_service_impl_unittest.cc
+++ b/content/browser/aggregation_service/aggregation_service_impl_unittest.cc
@@ -175,11 +175,7 @@
                         /*payload=*/kEFGH5678AsBytes,
                         /*key_id=*/"key_2");
 
-  AggregatableReportSharedInfo shared_info(
-      base::Time::FromJavaTime(1234567890123),
-      /*privacy_budget_key=*/"example_pbk");
-
-  AggregatableReport report(std::move(payloads), std::move(shared_info));
+  AggregatableReport report(std::move(payloads), "example_shared_info");
   assembler()->TriggerResponse(
       /*report_id=*/0, std::move(report),
       AggregatableReportAssembler::AssemblyStatus::kOk);
@@ -215,11 +211,7 @@
                         /*payload=*/kEFGH5678AsBytes,
                         /*key_id=*/"key_2");
 
-  AggregatableReportSharedInfo shared_info(
-      base::Time::FromJavaTime(1234567890123),
-      /*privacy_budget_key=*/"example_pbk");
-
-  AggregatableReport report(std::move(payloads), std::move(shared_info));
+  AggregatableReport report(std::move(payloads), "example_shared_info");
 
   SendReport(GURL("https://example.com/reports"), report);
 
diff --git a/content/browser/aggregation_service/aggregation_service_test_utils.cc b/content/browser/aggregation_service/aggregation_service_test_utils.cc
index ba94f12..8c1ee9e 100644
--- a/content/browser/aggregation_service/aggregation_service_test_utils.cc
+++ b/content/browser/aggregation_service/aggregation_service_test_utils.cc
@@ -86,7 +86,13 @@
     }
   }
 
-  return SharedInfoEqual(expected.shared_info(), actual.shared_info());
+  if (expected.shared_info() != actual.shared_info()) {
+    return testing::AssertionFailure()
+           << "Expected shared info " << expected.shared_info()
+           << ", actual: " << actual.shared_info();
+  }
+
+  return testing::AssertionSuccess();
 }
 
 testing::AssertionResult ReportRequestsEqual(
diff --git a/content/browser/devtools/auction_worklet_devtools_agent_host.cc b/content/browser/devtools/auction_worklet_devtools_agent_host.cc
index 4513d63..9774be01 100644
--- a/content/browser/devtools/auction_worklet_devtools_agent_host.cc
+++ b/content/browser/devtools/auction_worklet_devtools_agent_host.cc
@@ -72,17 +72,25 @@
 
 bool AuctionWorkletDevToolsAgentHost::AttachSession(DevToolsSession* session,
                                                     bool acquire_wake_lock) {
+  // We use `force_using_io_session` as AuctionV8DevToolsSession can't handle
+  // commands on main session when blocked on a breakpoint, only on IO session.
+  session->AttachToAgent(associated_agent_remote_.get(),
+                         /*force_using_io_session=*/true);
   return true;
 }
 
 AuctionWorkletDevToolsAgentHost::AuctionWorkletDevToolsAgentHost(
     DebuggableAuctionWorklet* worklet)
     : DevToolsAgentHostImpl(base::GenerateGUID()), worklet_(worklet) {
-  mojo::PendingRemote<blink::mojom::DevToolsAgent> agent;
-  worklet->ConnectDevToolsAgent(agent.InitWithNewPipeAndPassReceiver());
+  mojo::PendingAssociatedRemote<blink::mojom::DevToolsAgent> agent;
+  worklet->ConnectDevToolsAgent(agent.InitWithNewEndpointAndPassReceiver());
   NotifyCreated();
-  GetRendererChannel()->SetRenderer(std::move(agent), mojo::NullReceiver(),
-                                    ChildProcessHost::kInvalidUniqueID);
+
+  associated_agent_remote_.Bind(std::move(agent));
+
+  // Since we were just created, we don't have any sessions yet, so nothing to
+  // attach here.
+  DCHECK(sessions().empty());
 }
 
 AuctionWorkletDevToolsAgentHost::~AuctionWorkletDevToolsAgentHost() = default;
@@ -90,8 +98,7 @@
 void AuctionWorkletDevToolsAgentHost::WorkletDestroyed() {
   worklet_ = nullptr;
   ForceDetachAllSessions();
-  GetRendererChannel()->SetRenderer(mojo::NullRemote(), mojo::NullReceiver(),
-                                    ChildProcessHost::kInvalidUniqueID);
+  associated_agent_remote_.reset();
 }
 
 AuctionWorkletDevToolsAgentHostManager&
diff --git a/content/browser/devtools/auction_worklet_devtools_agent_host.h b/content/browser/devtools/auction_worklet_devtools_agent_host.h
index ab2f773f..dd58ffa5 100644
--- a/content/browser/devtools/auction_worklet_devtools_agent_host.h
+++ b/content/browser/devtools/auction_worklet_devtools_agent_host.h
@@ -13,6 +13,7 @@
 #include "content/browser/devtools/devtools_agent_host_impl.h"
 #include "content/browser/interest_group/debuggable_auction_worklet.h"
 #include "content/browser/interest_group/debuggable_auction_worklet_tracker.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -48,6 +49,7 @@
   bool AttachSession(DevToolsSession* session, bool acquire_wake_lock) override;
 
   DebuggableAuctionWorklet* worklet_ = nullptr;
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> associated_agent_remote_;
 };
 
 class AuctionWorkletDevToolsAgentHostManager
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index 88b3e2e..f009df6 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -461,11 +461,15 @@
 void AuctionRunner::OnBidScored(
     BidState* state,
     double score,
+    uint32_t data_version,
+    bool has_data_version,
     const absl::optional<GURL>& debug_loss_report_url,
     const absl::optional<GURL>& debug_win_report_url,
     const std::vector<std::string>& errors) {
   DCHECK_EQ(state->state, BidState::State::kSellerScoringBid);
   state->seller_score = score;
+  if (has_data_version)
+    state->data_version = data_version;
   --outstanding_bids_;
   state->state = BidState::State::kScoringComplete;
   // If `debug_loss_report_url` or `debug_win_report_url` is not a valid HTTPS
@@ -584,7 +588,8 @@
       auction_config_->auction_ad_config_non_shared_params.Clone(),
       top_bidder_->bidder.interest_group.owner,
       top_bidder_->bid_result->render_url, top_bidder_->bid_result->bid,
-      top_bidder_->seller_score,
+      top_bidder_->seller_score, top_bidder_->data_version.value_or(0),
+      top_bidder_->data_version.has_value(),
       base::BindOnce(&AuctionRunner::OnReportSellerResultComplete,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index 2d97fa39..5fa57ec 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -212,6 +212,8 @@
     // forDebuggingOnly.reportAdAuctionWin(url) called in scoreAd().
     absl::optional<GURL> seller_debug_loss_report_url;
     absl::optional<GURL> seller_debug_win_report_url;
+
+    absl::optional<uint32_t> data_version;
   };
 
   AuctionRunner(AuctionWorkletManager* auction_worklet_manager,
@@ -292,6 +294,8 @@
   // Callback from ScoreBid().
   void OnBidScored(BidState* state,
                    double score,
+                   uint32_t data_version,
+                   bool has_data_version,
                    const absl::optional<GURL>& debug_loss_report_url,
                    const absl::optional<GURL>& debug_win_report_url,
                    const std::vector<std::string>& errors);
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index b00c390..f25ce03 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -291,6 +291,8 @@
         throw new Error("wrong decisionLogicUrl in auctionConfig");
       if (browserSignals.topWindowHostname !== 'publisher1.com')
         throw new Error("wrong topWindowHostname in browserSignals");
+      if (browserSignals.dataVersion !== undefined)
+        throw new Error("wrong dataVersion in browserSignals");
       if (sendReportUrl)
         sendReportTo(sendReportUrl + browserSignals.bid);
       return browserSignals;
@@ -446,7 +448,8 @@
   }
 
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override {
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override {
     ADD_FAILURE()
         << "ConnectDevToolsAgent should not be called on MockBidderWorklet";
   }
@@ -594,6 +597,8 @@
                     const GURL& browser_signal_render_url,
                     double browser_signal_bid,
                     double browser_signal_desirability,
+                    uint32_t browser_signal_data_version,
+                    bool browser_signal_has_data_version,
                     ReportResultCallback report_result_callback) override {
     report_result_callback_ = std::move(report_result_callback);
     if (report_result_run_loop_)
@@ -601,7 +606,8 @@
   }
 
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override {
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override {
     ADD_FAILURE()
         << "ConnectDevToolsAgent should not be called on MockSellerWorklet";
   }
@@ -1559,13 +1565,14 @@
     task_environment_.RunUntilIdle();
 
     bool found = false;
-    mojo::Remote<blink::mojom::DevToolsAgent> agent;
+    mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
 
     for (DebuggableAuctionWorklet* debuggable :
          DebuggableAuctionWorkletTracker::GetInstance()->GetAll()) {
       if (debuggable->url() == debug_url) {
         found = true;
-        debuggable->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+        debuggable->ConnectDevToolsAgent(
+            agent.BindNewEndpointAndPassReceiver());
       }
     }
     ASSERT_TRUE(found);
@@ -1626,12 +1633,13 @@
     if (debug_url == kBidder2Url) {
       task_environment_.RunUntilIdle();
       found = false;
-      mojo::Remote<blink::mojom::DevToolsAgent> agent;
+      mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
       for (DebuggableAuctionWorklet* debuggable :
            DebuggableAuctionWorkletTracker::GetInstance()->GetAll()) {
         if (debuggable->url() == debug_url) {
           found = true;
-          debuggable->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+          debuggable->ConnectDevToolsAgent(
+              agent.BindNewEndpointAndPassReceiver());
         }
       }
       ASSERT_TRUE(found);
@@ -2452,8 +2460,8 @@
 
   // scoreAd() that only accepts bids where the scoring signals of the
   // `renderUrl` is "accept".
-  auction_worklet::AddJavascriptResponse(&url_loader_factory_,
-                                         kSellerUrl, std::string(R"(
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         std::string(R"(
 function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals,
                  browserSignals) {
   let signal = trustedScoringSignals.renderUrl[browserSignals.renderUrl];
@@ -2464,11 +2472,19 @@
     return 0;
   throw "incorrect trustedScoringSignals";
 }
-                                         )") + kBasicReportResult);
+
+function reportResult(auctionConfig, browserSignals) {
+  sendReportTo("https://reporting.example.com/" + browserSignals.bid);
+  if (browserSignals.dataVersion !== 2) {
+    throw new Error("wrong dataVersion in browserSignals");
+  }
+  return browserSignals;
+}
+                                         )"));
 
   // Only accept first bidder's bid. Requests will always be batched non-racily,
   // since a mock time is in use.
-  auction_worklet::AddJsonResponse(
+  auction_worklet::AddVersionedJsonResponse(
       &url_loader_factory_,
       GURL(trusted_scoring_signals_url_->spec() +
            "?hostname=publisher1.com"
@@ -2477,7 +2493,8 @@
            "https%3A%2F%2Fad2.com-component1.com%2F"),
       R"(
 {"renderUrls":{"https://ad1.com/":"accept", "https://ad2.com/":"reject"}}
-      )");
+      )",
+      /*data_version=*/2);
 
   RunStandardAuction();
   EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
@@ -2882,7 +2899,8 @@
     EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
     EXPECT_EQ(7, score_ad_params.bid);
     std::move(score_ad_params.callback)
-        .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+        .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+             /*debug_loss_report_url=*/absl::nullopt,
              /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
     // Finish the auction.
@@ -2946,7 +2964,8 @@
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(7, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+      .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+           /*debug_loss_report_url=*/absl::nullopt,
            /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
   // Score Bidder2's bid.
@@ -2954,7 +2973,8 @@
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+      .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+           /*debug_loss_report_url=*/absl::nullopt,
            /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
   // Bidder1 crashes while running ReportWin.
@@ -3065,14 +3085,16 @@
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(5, score_ad_params.bid);
       std::move(score_ad_params.callback)
-          .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+          .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+               /*debug_loss_report_url=*/absl::nullopt,
                /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
       // Score Bidder2's bid.
       EXPECT_EQ(kBidder2, score_ad_params2.interest_group_owner);
       EXPECT_EQ(7, score_ad_params2.bid);
       std::move(score_ad_params2.callback)
-          .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+          .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+               /*debug_loss_report_url=*/absl::nullopt,
                /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
       seller_worklet->WaitForReportResult();
@@ -3147,7 +3169,8 @@
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(1, score_ad_params.bid);
       std::move(score_ad_params.callback)
-          .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+          .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+               /*debug_loss_report_url=*/absl::nullopt,
                /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
       // Finish the auction.
@@ -3229,7 +3252,8 @@
       EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
       EXPECT_EQ(1, score_ad_params.bid);
       std::move(score_ad_params.callback)
-          .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+          .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+               /*debug_loss_report_url=*/absl::nullopt,
                /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
       // Finish the auction.
@@ -3447,7 +3471,8 @@
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+      .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+           /*debug_loss_report_url=*/absl::nullopt,
            /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
   // Bidder1 never gets to report anything, since the seller providing a bad
@@ -3495,7 +3520,8 @@
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+      .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+           /*debug_loss_report_url=*/absl::nullopt,
            /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
   seller_worklet->WaitForReportResult();
@@ -3550,7 +3576,8 @@
   EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
   EXPECT_EQ(7, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/11, /*debug_loss_report_url=*/absl::nullopt,
+      .Run(/*score=*/11, /*data_version=*/0, /*has_data_version=*/false,
+           /*debug_loss_report_url=*/absl::nullopt,
            /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
   // Finish the auction.
@@ -3604,7 +3631,8 @@
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
     std::move(score_ad_params.callback)
-        .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+        .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+             /*debug_loss_report_url=*/absl::nullopt,
              /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
 
     // Bidder2 returns a bid, which is then scored.
@@ -3614,7 +3642,8 @@
     EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
     std::move(score_ad_params.callback)
-        .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+        .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+             /*debug_loss_report_url=*/absl::nullopt,
              /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
     // Need to flush the service pipe to make sure the AuctionRunner has
     // received the score.
@@ -3734,6 +3763,7 @@
           case Event::kBid1Scored:
             std::move(score_ad_params1.callback)
                 .Run(/*score=*/bidder1_wins ? 11 : 9,
+                     /*data_version=*/0, /*has_data_version=*/false,
                      /*debug_loss_report_url=*/absl::nullopt,
                      /*debug_win_report_url=*/absl::nullopt, /*errors=*/{});
             // Wait for the AuctionRunner to receive the score.
@@ -3741,7 +3771,9 @@
             break;
           case Event::kBid2Scored:
             std::move(score_ad_params2.callback)
-                .Run(/*score=*/10, /*debug_loss_report_url=*/absl::nullopt,
+                .Run(/*score=*/10, /*data_version=*/0,
+                     /*has_data_version=*/false,
+                     /*debug_loss_report_url=*/absl::nullopt,
                      /*debug_win_report_url=*/absl::nullopt,
                      /*errors=*/{});
             // Wait for the AuctionRunner to receive the score.
@@ -4011,7 +4043,8 @@
     EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
     EXPECT_EQ(5, score_ad_params.bid);
     std::move(score_ad_params.callback)
-        .Run(/*score=*/10, test_case.seller_debug_loss_report_url,
+        .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+             test_case.seller_debug_loss_report_url,
              test_case.seller_debug_win_report_url, /*errors=*/{});
     auction_run_loop_->Run();
     EXPECT_EQ(test_case.expected_error_message, TakeBadMessage());
@@ -4064,7 +4097,8 @@
   EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
   EXPECT_EQ(5, score_ad_params.bid);
   std::move(score_ad_params.callback)
-      .Run(/*score=*/10, GURL("https://seller-debug-loss-reporting.com/1"),
+      .Run(/*score=*/10, /*data_version=*/0, /*has_data_version=*/false,
+           GURL("https://seller-debug-loss-reporting.com/1"),
            GURL("https://seller-debug-win-reporting.com/1"), /*errors=*/{});
 
   seller_worklet->WaitForReportResult();
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index 9bf28c4..e14f9fbd 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -153,7 +153,8 @@
   }
 
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override {
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override {
     ADD_FAILURE()
         << "ConnectDevToolsAgent should not be called on MockBidderWorklet";
   }
@@ -276,12 +277,15 @@
                     const GURL& browser_signal_render_url,
                     double browser_signal_bid,
                     double browser_signal_desirability,
+                    uint32_t browser_signal_data_version,
+                    bool browser_signal_has_data_version,
                     ReportResultCallback report_result_callback) override {
     NOTREACHED();
   }
 
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override {
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override {
     ADD_FAILURE()
         << "ConnectDevToolsAgent should not be called on MockSellerWorklet";
   }
diff --git a/content/browser/interest_group/debuggable_auction_worklet.cc b/content/browser/interest_group/debuggable_auction_worklet.cc
index ea6e78ce..6348abc 100644
--- a/content/browser/interest_group/debuggable_auction_worklet.cc
+++ b/content/browser/interest_group/debuggable_auction_worklet.cc
@@ -22,7 +22,7 @@
 }
 
 void DebuggableAuctionWorklet::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) {
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent) {
   if (auction_worklet::mojom::BidderWorklet** bidder_worklet =
           absl::get_if<auction_worklet::mojom::BidderWorklet*>(&worklet_)) {
     (*bidder_worklet)->ConnectDevToolsAgent(std::move(agent));
diff --git a/content/browser/interest_group/debuggable_auction_worklet.h b/content/browser/interest_group/debuggable_auction_worklet.h
index 6bca4b5..5590659 100644
--- a/content/browser/interest_group/debuggable_auction_worklet.h
+++ b/content/browser/interest_group/debuggable_auction_worklet.h
@@ -11,7 +11,7 @@
 #include "content/common/content_export.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom-forward.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-forward.h"
 #include "url/gurl.h"
@@ -36,7 +36,7 @@
   std::string Title() const;
 
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent);
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent);
 
   // Returns true if the worklet should start in the paused state.
   bool should_pause_on_start() const { return should_pause_on_start_; }
diff --git a/content/browser/isolation_context.h b/content/browser/isolation_context.h
index e08f500..be85a851 100644
--- a/content/browser/isolation_context.h
+++ b/content/browser/isolation_context.h
@@ -70,9 +70,9 @@
 
  private:
   // When non-null, associates this context with a particular BrowsingInstance.
-  BrowsingInstanceId browsing_instance_id_;
+  const BrowsingInstanceId browsing_instance_id_;
 
-  BrowserOrResourceContext browser_or_resource_context_;
+  const BrowserOrResourceContext browser_or_resource_context_;
 
   // Specifies whether the BrowsingInstance associated with this context is for
   // a <webview> guest.
diff --git a/content/browser/quota/quota_internals_ui.cc b/content/browser/quota/quota_internals_ui.cc
index 23b9447..aa216a5 100644
--- a/content/browser/quota/quota_internals_ui.cc
+++ b/content/browser/quota/quota_internals_ui.cc
@@ -5,7 +5,8 @@
 #include "content/browser/quota/quota_internals_ui.h"
 
 #include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "content/grit/dev_ui_content_resources.h"
+#include "content/grit/quota_internals_resources.h"
+#include "content/grit/quota_internals_resources_map.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -19,11 +20,10 @@
   WebUIDataSource* source =
       WebUIDataSource::Create(kChromeUIQuotaInternalsHost);
 
-  source->AddResourcePath("quota_internals.mojom-webui.js",
-                          IDR_QUOTA_INTERNALS_MOJOM_JS);
-  source->AddResourcePath("quota_internals.js", IDR_QUOTA_INTERNALS_JS);
-  source->AddResourcePath("quota-internals", IDR_QUOTA_INTERNALS_HTML);
-  source->SetDefaultResource(IDR_QUOTA_INTERNALS_HTML);
+  source->AddResourcePaths(
+      base::make_span(kQuotaInternalsResources, kQuotaInternalsResourcesSize));
+
+  source->SetDefaultResource(IDR_QUOTA_INTERNALS_QUOTA_INTERNALS_HTML);
 
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ScriptSrc,
diff --git a/content/browser/resources/BUILD.gn b/content/browser/resources/BUILD.gn
index ab4c080c..2b6281e 100644
--- a/content/browser/resources/BUILD.gn
+++ b/content/browser/resources/BUILD.gn
@@ -4,13 +4,16 @@
 
 import("//third_party/closure_compiler/compile_js.gni")
 
+group("resources") {
+  public_deps = [ "quota:resources" ]
+}
+
 if (enable_js_type_check) {
   group("closure_compile") {
     deps = [
       "attribution_reporting:closure_compile",
       "histograms:closure_compile",
       "process:closure_compile",
-      "quota:closure_compile",
     ]
   }
 }
diff --git a/content/browser/resources/quota/BUILD.gn b/content/browser/resources/quota/BUILD.gn
index 12a40d7..5d19fd9 100644
--- a/content/browser/resources/quota/BUILD.gn
+++ b/content/browser/resources/quota/BUILD.gn
@@ -2,29 +2,70 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
+import("//chrome/common/features.gni")
+import("//tools/grit/grit_rule.gni")
+import("//tools/grit/preprocess_if_expr.gni")
+import("//tools/polymer/html_to_js.gni")
+import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
-js_type_check("closure_compile") {
-  closure_flags = default_closure_args + mojom_js_args + [
-                    "js_module_root=" + rebase_path(".", root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/storage/browser/quota",
-                            root_build_dir),
-                  ]
-  deps = [ ":quota_internals" ]
+generate_grd("build_grd") {
+  grd_prefix = "quota_internals"
+  out_grd = "$target_gen_dir/resources.grd"
+  input_files = [ "quota_internals.html" ]
+  input_files_base_dir = rebase_path(".", "//")
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
 }
 
-js_library("quota_internals") {
-  sources = [ "quota_internals.js" ]
-  deps = [
-    "//storage/browser/quota:mojo_bindings_webui_js",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:util.m",
-    "//ui/webui/resources/js/cr:event_target.m",
-    "//ui/webui/resources/js/cr:ui.m",
-    "//ui/webui/resources/js/cr/ui:array_data_model.m",
-    "//ui/webui/resources/js/cr/ui:tabs",
-    "//ui/webui/resources/js/cr/ui:tree",
+preprocess_if_expr("preprocess_mojo") {
+  deps = [ "//storage/browser/quota:mojo_bindings_webui_js" ]
+  in_folder = "$root_gen_dir/mojom-webui/storage/browser/quota/"
+  out_folder = "$target_gen_dir"
+  in_files = [ "quota_internals.mojom-webui.js" ]
+}
+
+preprocess_if_expr("preprocess_proxy") {
+  in_folder = "./"
+  out_folder = "$target_gen_dir"
+  in_files = [ "quota_internals_browser_proxy.ts" ]
+}
+
+grit("resources") {
+  defines = chrome_grit_defines
+
+  # These arguments are needed since the grd is generated at build time.
+  enable_input_discovery_for_gn_analyze = false
+  source = "$target_gen_dir/resources.grd"
+  deps = [ ":build_grd" ]
+
+  outputs = [
+    "grit/quota_internals_resources.h",
+    "grit/quota_internals_resources_map.cc",
+    "grit/quota_internals_resources_map.h",
+    "quota_internals_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/content"
+}
+
+html_to_js("web_components") {
+  js_files = [ "quota_internals.ts" ]
+}
+
+ts_library("build_ts") {
+  root_dir = "$target_gen_dir"
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  in_files = [
+    "quota_internals.ts",
+    "quota_internals_browser_proxy.ts",
+    "quota_internals.mojom-webui.js",
+  ]
+  deps = [ "//ui/webui/resources:library" ]
+  extra_deps = [
+    ":preprocess_mojo",
+    ":preprocess_proxy",
+    ":web_components",
   ]
 }
diff --git a/content/browser/resources/quota/quota_internals.html b/content/browser/resources/quota/quota_internals.html
index 09be6533..e8f298a 100644
--- a/content/browser/resources/quota/quota_internals.html
+++ b/content/browser/resources/quota/quota_internals.html
@@ -7,7 +7,7 @@
   <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
   <link rel="stylesheet" href="chrome://resources/css/roboto.css">
   <link rel="stylesheet" href="quota_internals.css">
-  <script type="module" src="quota_internals.js"></script>
+  <script type="module" src="quota_internals.js" defer></script>
 </head>
 <body>
   <h1>Quota Internals 2</h1>
diff --git a/content/browser/resources/quota/quota_internals.js b/content/browser/resources/quota/quota_internals.js
deleted file mode 100644
index 6605851..0000000
--- a/content/browser/resources/quota/quota_internals.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2021 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 {$} from 'chrome://resources/js/util.m.js';
-import {QuotaInternalsHandler, QuotaInternalsHandlerRemote} from './quota_internals.mojom-webui.js';
-
-/**
- * @param response
- */
-function renderDiskAvailability(response) {
-  const rowTemplate = /** @type {!HTMLTemplateElement} */ ($('listener-row'));
-  const tableBody = /** @type {!HTMLTableElement} */ ($('listeners-tbody'));
-  const listenerRowTemplate =
-      /** @type {!HTMLTemplateElement} */ (rowTemplate.cloneNode(true));
-  const listenerRow = listenerRowTemplate.content;
-
-  const availableSpaceBytes =
-      parseFloat(Number(response.availableSpace) / (1024 ** 3)).toFixed(2);
-  const totalSpaceBytes =
-      parseFloat(Number(response.totalSpace) / (1024 ** 3)).toFixed(2);
-
-  tableBody.innerHTML = trustedTypes.emptyHTML;
-
-  listenerRow.querySelector('.total-space').textContent =
-      `${totalSpaceBytes} GB`;
-  listenerRow.querySelector('.available-space').textContent =
-      `${availableSpaceBytes} GB`;
-
-  tableBody.append(listenerRow);
-}
-
-/**
- * @param response
- */
-function renderEvictionStats(response) {
-  const rowTemplate = /** @type {!HTMLTemplateElement} */ ($('eviction-row'));
-  const evictionRowTemplate =
-      /** @type {!HTMLTemplateElement} */ (rowTemplate.cloneNode(true));
-  const evictionRow = evictionRowTemplate.content;
-
-  const tableBody = /** @type {!HTMLTableElement} */ ($('eviction-tbody'));
-  tableBody.innerHTML = trustedTypes.emptyHTML;
-
-  evictionRow.querySelector('.errors-on-getting-usage-and-quota').textContent =
-      response.evictionStatistics['errors-on-getting-usage-and-quota'];
-  evictionRow.querySelector('.evicted-buckets').textContent =
-      response.evictionStatistics['evicted-buckets'];
-  evictionRow.querySelector('.eviction-rounds').textContent =
-      response.evictionStatistics['eviction-rounds'];
-  evictionRow.querySelector('.skipped-eviction-rounds').textContent =
-      response.evictionStatistics['skipped-eviction-rounds'];
-
-  tableBody.appendChild(evictionRow);
-}
-
-document.addEventListener('DOMContentLoaded', () => {
-  /**
-   * @type {!QuotaInternalsHandlerRemote}
-   */
-  const pageHandler = QuotaInternalsHandler.getRemote();
-
-  pageHandler.getDiskAvailability().then(renderDiskAvailability);
-  pageHandler.getStatistics().then(renderEvictionStats);
-
-});
\ No newline at end of file
diff --git a/content/browser/resources/quota/quota_internals.ts b/content/browser/resources/quota/quota_internals.ts
new file mode 100644
index 0000000..5ea2d61
--- /dev/null
+++ b/content/browser/resources/quota/quota_internals.ts
@@ -0,0 +1,60 @@
+// Copyright 2022 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 {QuotaInternalsBrowserProxy} from './quota_internals_browser_proxy.js';
+
+function getProxy(): QuotaInternalsBrowserProxy {
+  return QuotaInternalsBrowserProxy.getInstance();
+}
+
+async function renderDiskAvailability() {
+  const result = await getProxy().getDiskAvailability();
+
+  const rowTemplate: HTMLTemplateElement =
+      document.body.querySelector<HTMLTemplateElement>('#listener-row')!;
+  const tableBody: HTMLTableElement =
+      document.body.querySelector<HTMLTableElement>('#listeners-tbody')!;
+  const listenerRowTemplate: HTMLTemplateElement =
+      rowTemplate.cloneNode(true) as HTMLTemplateElement;
+  const listenerRow = listenerRowTemplate.content;
+
+  const availableSpaceBytes =
+      (Number(result.availableSpace) / (1024 ** 3)).toFixed(2);
+  const totalSpaceBytes = (Number(result.totalSpace) / (1024 ** 3)).toFixed(2);
+
+  listenerRow.querySelector('.total-space')!.textContent =
+      `${totalSpaceBytes} GB`;
+  listenerRow.querySelector('.available-space')!.textContent =
+      `${availableSpaceBytes} GB`;
+
+  tableBody.append(listenerRow);
+}
+
+async function renderEvictionStats() {
+  const result = await getProxy().getStatistics();
+
+  const rowTemplate: HTMLTemplateElement =
+      document.body.querySelector<HTMLTemplateElement>('#eviction-row')!;
+  const tableBody: HTMLTableElement =
+      document.body.querySelector<HTMLTableElement>('#eviction-tbody')!;
+  const evictionRowTemplate: HTMLTemplateElement =
+      rowTemplate.cloneNode(true) as HTMLTemplateElement;
+  const evictionRow = evictionRowTemplate.content;
+
+  evictionRow.querySelector('.errors-on-getting-usage-and-quota')!.textContent =
+      result.evictionStatistics['errors-on-getting-usage-and-quota'];
+  evictionRow.querySelector('.evicted-buckets')!.textContent =
+      result.evictionStatistics['evicted-buckets'];
+  evictionRow.querySelector('.eviction-rounds')!.textContent =
+      result.evictionStatistics['eviction-rounds'];
+  evictionRow.querySelector('.skipped-eviction-rounds')!.textContent =
+      result.evictionStatistics['skipped-eviction-rounds'];
+
+  tableBody.appendChild(evictionRow);
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+  renderDiskAvailability();
+  renderEvictionStats();
+});
\ No newline at end of file
diff --git a/content/browser/resources/quota/quota_internals_browser_proxy.ts b/content/browser/resources/quota/quota_internals_browser_proxy.ts
new file mode 100644
index 0000000..2c8f5da
--- /dev/null
+++ b/content/browser/resources/quota/quota_internals_browser_proxy.ts
@@ -0,0 +1,36 @@
+// Copyright 2022 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 {QuotaInternalsHandler, QuotaInternalsHandlerRemote} from './quota_internals.mojom-webui.js';
+
+type GetDiskAvailabilityResult = {
+  totalSpace: bigint, availableSpace: bigint;
+};
+
+type GetStatisticsResult = {
+  evictionStatistics: {
+    'errors-on-getting-usage-and-quota': string,
+    'evicted-buckets': string,
+    'eviction-rounds': string,
+    'skipped-eviction-rounds': string
+  }
+};
+
+export class QuotaInternalsBrowserProxy {
+  private handler = QuotaInternalsHandler.getRemote();
+
+  getDiskAvailability(): Promise<GetDiskAvailabilityResult> {
+    return this.handler.getDiskAvailability();
+  }
+
+  getStatistics(): Promise<GetStatisticsResult> {
+    return this.handler.getStatistics();
+  }
+
+  static getInstance(): QuotaInternalsBrowserProxy {
+    return instance || (instance = new QuotaInternalsBrowserProxy());
+  }
+}
+
+let instance: QuotaInternalsBrowserProxy|null = null;
\ No newline at end of file
diff --git a/content/browser/resources/quota/tsconfig_base.json b/content/browser/resources/quota/tsconfig_base.json
new file mode 100644
index 0000000..6c1e906a
--- /dev/null
+++ b/content/browser/resources/quota/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+    "extends": "../../../../tools/typescript/tsconfig_base.json",
+    "compilerOptions": {
+      "allowJs": true,
+      "noUncheckedIndexedAccess": false,
+      "noUnusedLocals": false,
+      "strictPropertyInitialization": false
+    }
+  }
\ No newline at end of file
diff --git a/content/browser/webid/fedcm_metrics.h b/content/browser/webid/fedcm_metrics.h
index cb31426b..5b2de2d 100644
--- a/content/browser/webid/fedcm_metrics.h
+++ b/content/browser/webid/fedcm_metrics.h
@@ -82,14 +82,10 @@
     ukm::SourceId source_id);
 
 // Records the status of the |RequestIdToken| call.
-// TODO(yigu): Call this function from |CompleteRequest| once the mojom side
-// |RequestIdTokenStatus| is cleaned up.
 void RecordRequestIdTokenStatus(FedCmRequestIdTokenStatus status,
                                 ukm::SourceId source_id);
 
 // Records the status of the |Revoke| call.
-// TODO(yigu): Call this function from |CompleteRevokeRequest| once the mojom
-// side |RevokeStatus| is cleaned up.
 void RecordRevokeStatus(FedCmRevokeStatus status, ukm::SourceId source_id);
 }  // namespace content
 
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index a970716..244c1e0b 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -895,6 +895,16 @@
 
   CleanUp();
 
+  // Avoids exposing to renderer detailed error messages which may leak cross
+  // site information to the API call site.
+  // TODO(yigu): Update RequestIdTokenStatus to only include generic errors to
+  // send to the renderer. Meanwhile, keep the detailed status which is used in
+  // metrics and devtools in content/.
+  if (status >= RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound &&
+      status <= RequestIdTokenStatus::kErrorFetchingIdTokenInvalidRequest) {
+    status = RequestIdTokenStatus::kError;
+  }
+
   if (auth_request_callback_)
     std::move(auth_request_callback_).Run(status, id_token);
 }
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 4fa8c853..6be46ce 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -103,6 +103,7 @@
 // Expected return values from a call to RequestIdToken.
 typedef struct {
   RequestIdTokenStatus return_status;
+  RequestIdTokenStatus devtools_issue_status;
   const char* token;
 } RequestExpectations;
 
@@ -165,7 +166,7 @@
 static const AuthRequestTestCase kPermissionTestCases[]{
     {"Successful run with the IdP page loaded",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kSuccess, kToken},
+     {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
      {kToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -179,7 +180,7 @@
 
     {"Successful run with a token response from the idp_endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kSuccess, kToken},
+     {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
      {kToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -193,25 +194,29 @@
 
     {"Initial user permission denied",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kApprovalDeclined, kEmptyToken},
+     {RequestIdTokenStatus::kApprovalDeclined,
+      RequestIdTokenStatus::kApprovalDeclined, kEmptyToken},
      {kToken, UserApproval::kDenied, absl::nullopt, absl::nullopt, "", "", "",
       "", kPermissionNoop, kMediatedNoop}},
 
     {"Wellknown file not found",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound, kEmptyToken},
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound, kEmptyToken},
      {kToken, UserApproval::kApproved, FetchStatus::kHttpNotFoundError,
       absl::nullopt, "", "", "", "", kPermissionNoop, kMediatedNoop}},
 
     {"Wellknown fetch error",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse, kEmptyToken},
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse, kEmptyToken},
      {kToken, UserApproval::kApproved, FetchStatus::kNoResponseError,
       absl::nullopt, "", "", "", "", kPermissionNoop, kMediatedNoop}},
 
     {"Error parsing wellknown for Permission mode",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
       kEmptyToken},
      {kToken, UserApproval::kApproved, FetchStatus::kInvalidResponseError,
       absl::nullopt, "", kAccountsEndpoint, kTokenEndpoint, "", kPermissionNoop,
@@ -219,7 +224,8 @@
 
     {"Error reaching the idpendpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFetchingSignin, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingSignin,
+      RequestIdTokenStatus::kErrorFetchingSignin, kEmptyToken},
      {kToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -233,7 +239,8 @@
 
     {"Error parsing the idpendpoint response",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorInvalidSigninResponse, kEmptyToken},
+     {RequestIdTokenStatus::kErrorInvalidSigninResponse,
+      RequestIdTokenStatus::kErrorInvalidSigninResponse, kEmptyToken},
      {kToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -247,7 +254,7 @@
 
     {"IdP window closed before token provision",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kError, kEmptyToken},
+     {RequestIdTokenStatus::kError, RequestIdTokenStatus::kError, kEmptyToken},
      {kEmptyToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -261,7 +268,8 @@
 
     {"Token provision declined by user after IdP window closed",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kApprovalDeclined, kEmptyToken},
+     {RequestIdTokenStatus::kApprovalDeclined,
+      RequestIdTokenStatus::kApprovalDeclined, kEmptyToken},
      {kToken,
       UserApproval::kApproved,
       FetchStatus::kSuccess,
@@ -276,7 +284,8 @@
 static const AuthRequestTestCase kMediatedTestCases[]{
     {"Error parsing wellknown for Mediated mode missing token endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
       kEmptyToken},
      {kToken, absl::nullopt, FetchStatus::kInvalidResponseError, absl::nullopt,
       kIdpEndpoint, kAccountsEndpoint, "", kClientIdMetadataEndpoint,
@@ -284,7 +293,8 @@
 
     {"Error parsing wellknown for Mediated mode missing accounts endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
       kEmptyToken},
      {kToken, absl::nullopt, FetchStatus::kInvalidResponseError, absl::nullopt,
       kIdpEndpoint, "", kTokenEndpoint, kClientIdMetadataEndpoint,
@@ -292,7 +302,8 @@
 
     {"Error reaching Accounts endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorFetchingAccountsNoResponse, kEmptyToken},
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingAccountsNoResponse, kEmptyToken},
      {kEmptyToken,
       absl::nullopt,
       FetchStatus::kSuccess,
@@ -306,7 +317,8 @@
 
     {"Error parsing Accounts response",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse, kEmptyToken},
+     {RequestIdTokenStatus::kError,
+      RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse, kEmptyToken},
      {kToken,
       absl::nullopt,
       FetchStatus::kSuccess,
@@ -320,7 +332,7 @@
 
     {"Successful Mediated flow",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kSuccess, kToken},
+     {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
      {kToken,
       absl::nullopt,
       FetchStatus::kSuccess,
@@ -846,9 +858,9 @@
   auto auth_response = PerformAuthRequest(
       test_case.inputs.client_id, test_case.inputs.nonce, test_case.inputs.mode,
       test_case.inputs.prefer_auto_sign_in);
-  EXPECT_EQ(
-      main_test_rfh()->GetFederatedAuthRequestIssueCount(auth_response.first),
-      auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1);
+  EXPECT_EQ(main_test_rfh()->GetFederatedAuthRequestIssueCount(
+                test_case.expected.devtools_issue_status),
+            auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1);
 }
 
 // Test Logout method success with multiple relying parties.
@@ -901,7 +913,7 @@
     "Successful mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated,
      kNotPreferAutoSignIn},
-    {RequestIdTokenStatus::kSuccess, kToken},
+    {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
     {kToken,
      absl::nullopt,
      FetchStatus::kSuccess,
@@ -917,7 +929,7 @@
     "Failed mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated,
      kNotPreferAutoSignIn},
-    {RequestIdTokenStatus::kSuccess, kToken},
+    {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
     {kToken,
      absl::nullopt,
      FetchStatus::kSuccess,
@@ -933,7 +945,7 @@
     "Successful mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated,
      kPreferAutoSignIn},
-    {RequestIdTokenStatus::kSuccess, kToken},
+    {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
     {kToken,
      absl::nullopt,
      FetchStatus::kSuccess,
@@ -1354,7 +1366,7 @@
       "Failed mediated flow due to user not selecting an account",
       {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated,
        kNotPreferAutoSignIn},
-      {RequestIdTokenStatus::kSuccess, kToken},
+      {RequestIdTokenStatus::kSuccess, RequestIdTokenStatus::kSuccess, kToken},
       {kToken,
        absl::nullopt,
        FetchStatus::kSuccess,
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 441d11d4..1a94fa9 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -356,11 +356,20 @@
 
   idp_well_known_callback_ = std::move(callback);
 
-  // TODO(yigu): Using .well-known in sub-directory (non-root) is common for multi-tenancy. However,
-  // this may be an invalid use of .well-known and we should enforce it to be under root.
-  // https://crbug.com/1277712.
-  GURL target_url =
-      provider_.Resolve(IdpNetworkRequestManager::kWellKnownFilePath);
+  // Accepts both "https://idp.example/foo/" and "https://idp.example/foo" as
+  // valid provider url to locate the manifest. Historically, URLs with a
+  // trailing slash indicate a directory while those without a trailing slash
+  // denote a file. However, to give developers more flexibility, we append a
+  // trailing slash if one is not present.
+  GURL target_url = provider_;
+  if (target_url.path().empty() || target_url.path().back() != '/') {
+    std::string new_path = target_url.path() + '/';
+    GURL::Replacements replacements;
+    replacements.SetPathStr(new_path);
+    target_url = target_url.ReplaceComponents(replacements);
+  }
+
+  target_url = target_url.Resolve(IdpNetworkRequestManager::kWellKnownFilePath);
 
   url_loader_ =
       CreateUncredentialedUrlLoader(target_url, /* send_referrer= */ false);
diff --git a/content/browser/webid/webid_browsertest.cc b/content/browser/webid/webid_browsertest.cc
index 1819416c..5c27fc0 100644
--- a/content/browser/webid/webid_browsertest.cc
+++ b/content/browser/webid/webid_browsertest.cc
@@ -301,8 +301,7 @@
   idp_server()->SetWellKnownResponseDetails({net::HTTP_NOT_FOUND, "", ""});
 
   std::string expected_error =
-      "a JavaScript error: \"NetworkError: The provider's .well-known "
-      "configuration cannot be found.\"\n";
+      "a JavaScript error: \"NetworkError: Error retrieving an id token.\"\n";
   EXPECT_EQ(expected_error, EvalJs(shell(), GetBasicRequestString()).error);
 }
 
diff --git a/content/dev_ui_content_resources.grd b/content/dev_ui_content_resources.grd
index edce3ae..eca376b 100644
--- a/content/dev_ui_content_resources.grd
+++ b/content/dev_ui_content_resources.grd
@@ -37,10 +37,6 @@
       <include name="IDR_PROCESS_INTERNALS_MOJO_JS" file="${root_gen_dir}/mojom-webui/content/browser/process_internals/process_internals.mojom-webui.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_PROCESS_INTERNALS_CSS" file="browser/resources/process/process_internals.css" type="BINDATA" />
       <include name="IDR_PROCESS_INTERNALS_JS" file="browser/resources/process/process_internals.js" type="BINDATA" />
-      <include name="IDR_QUOTA_INTERNALS_HTML" file="browser/resources/quota/quota_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-      <include name="IDR_QUOTA_INTERNALS_JS" file="browser/resources/quota/quota_internals.js" type="BINDATA" />
-      <include name="IDR_QUOTA_INTERNALS_CSS" file="browser/resources/quota/quota_internals.css" type="BINDATA" />
-      <include name="IDR_QUOTA_INTERNALS_MOJOM_JS" file="${root_gen_dir}/mojom-webui/storage/browser/quota/quota_internals.mojom-webui.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" type="BINDATA" />
       <include name="IDR_SERVICE_WORKER_INTERNALS_CSS" file="browser/resources/service_worker/serviceworker_internals.css" type="BINDATA" />
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
index e020bca..7548beb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
@@ -176,6 +176,7 @@
     @Override
     public void startBrowserProcessesAsync(@LibraryProcessType int libraryProcessType,
             boolean startGpuProcess, boolean startMinimalBrowser, final StartupCallback callback) {
+        assert !LibraryLoader.isBrowserProcessStartupBlockedForTesting();
         assertProcessTypeSupported(libraryProcessType);
         assert ThreadUtils.runningOnUiThread() : "Tried to start the browser on the wrong thread.";
         ServicificationStartupUma.getInstance().record(ServicificationStartupUma.getStartupMode(
@@ -237,6 +238,7 @@
     @Override
     public void startBrowserProcessesSync(
             @LibraryProcessType int libraryProcessType, boolean singleProcess) {
+        assert !LibraryLoader.isBrowserProcessStartupBlockedForTesting();
         assertProcessTypeSupported(libraryProcessType);
 
         ServicificationStartupUma.getInstance().record(ServicificationStartupUma.getStartupMode(
diff --git a/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java b/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
index 1703f2d..143c132 100644
--- a/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
@@ -100,7 +100,8 @@
      * Register a BroadcastReceiver in the given context.
      */
     public void registerReceiver(Context context) {
-        context.registerReceiver(getBroadcastReceiver(), getIntentFilter());
+        ContextUtils.registerExportedBroadcastReceiver(
+                context, getBroadcastReceiver(), getIntentFilter(), null);
     }
 
     /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java b/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
index ce93d441..0b206a71 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
@@ -17,6 +17,7 @@
 import com.google.android.gms.tasks.OnFailureListener;
 import com.google.android.gms.tasks.Task;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -45,7 +46,8 @@
         if (DEBUG) Log.d(TAG, "Registering intent filters.");
         IntentFilter filter = new IntentFilter();
         filter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
-        mContext.registerReceiver(this, filter, SmsRetriever.SEND_PERMISSION, null);
+        ContextUtils.registerExportedBroadcastReceiver(
+                mContext, this, filter, SmsRetriever.SEND_PERMISSION);
     }
 
     public SmsRetrieverClient createClient() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java b/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
index 39af827..eee78a2 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
@@ -21,6 +21,7 @@
 import com.google.android.gms.common.api.Status;
 import com.google.android.gms.tasks.Task;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.content.browser.sms.Wrappers.WebOTPServiceContext;
@@ -66,7 +67,12 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION);
 
-        mContext.registerReceiver(this, filter);
+        // The SEND_PERMISSION permission is not documented to held by the sender of this broadcast,
+        // but it's coming from the same place the UserConsent (SmsRetriever.SMS_RETRIEVED_ACTION)
+        // broadcast is coming from, so the sender will be holding this permission. This prevents
+        // other apps from spoofing verification codes.
+        ContextUtils.registerExportedBroadcastReceiver(
+                mContext, this, filter, SmsRetriever.SEND_PERMISSION);
     }
 
     public SmsCodeBrowserClient createClient() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java b/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
index d9ab214..7fc0a95 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
@@ -15,7 +15,6 @@
 
 import com.google.android.gms.auth.api.phone.SmsCodeBrowserClient;
 import com.google.android.gms.auth.api.phone.SmsCodeRetriever;
-import com.google.android.gms.auth.api.phone.SmsRetriever;
 import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
 import com.google.android.gms.tasks.Task;
 
@@ -82,30 +81,35 @@
             return new SmsVerificationReceiver(mSmsProviderGms, this);
         }
 
+        private void onRegisterReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+            if (filter.hasAction(SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION)) {
+                mVerificationReceiver = receiver;
+            } else {
+                mUserConsentReceiver = receiver;
+            }
+        }
+
         // ---------------------------------------------------------------------
         // Context overrides:
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                 String permission, Handler handler) {
-            assert filter.hasAction(SmsRetriever.SMS_RETRIEVED_ACTION);
-            mUserConsentReceiver = receiver;
-
+            onRegisterReceiver(receiver, filter);
             return super.registerReceiver(receiver, filter, permission, handler);
         }
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            assert filter.hasAction(SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION);
-            mVerificationReceiver = receiver;
-            return super.registerReceiver(receiver, filter);
+            throw new RuntimeException(); // Not implemented.
         }
 
         @Override
         @TargetApi(Build.VERSION_CODES.O)
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                 String permission, Handler handler, int flags) {
-            throw new RuntimeException(); // Not implemented.
+            onRegisterReceiver(receiver, filter);
+            return super.registerReceiver(receiver, filter, permission, handler);
         }
 
         @Override
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
index 065fd222..3252ece8c 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
@@ -30,7 +30,7 @@
 @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 @SuppressLint("VisibleForTests")
-@Batch(Batch.UNIT_TESTS)
+@Batch(Batch.PER_CLASS)
 public class WebContentsAccessibilityEventsTest {
     // File path that holds all the relevant tests.
     private static final String BASE_FILE_PATH = "content/test/data/accessibility/event/";
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
index 1fc8d03..f72a332f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -32,7 +32,7 @@
 @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 @SuppressLint("VisibleForTests")
-@Batch(Batch.UNIT_TESTS)
+@Batch(Batch.PER_CLASS)
 public class WebContentsAccessibilityTreeTest {
     // File path that holds all the relevant tests.
     private static final String BASE_ACCNAME_FILE_PATH = "content/test/data/accessibility/accname/";
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 3c99d11..58381e81 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -777,7 +777,7 @@
 // Enable the basic-card payment method from the PaymentRequest API. This flag
 // will be used to deprecate basic-card eventually: crbug.com/1209835.
 const base::Feature kPaymentRequestBasicCard{"PaymentRequestBasicCard",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Use this feature to experiment terminating a service worker when it doesn't
 // control any clients: https://crbug.com/1043845.
diff --git a/content/services/auction_worklet/BUILD.gn b/content/services/auction_worklet/BUILD.gn
index 07f8d53d..d8c2319 100644
--- a/content/services/auction_worklet/BUILD.gn
+++ b/content/services/auction_worklet/BUILD.gn
@@ -49,8 +49,6 @@
     "auction_worklet_service_impl.h",
     "bidder_worklet.cc",
     "bidder_worklet.h",
-    "console.cc",
-    "console.h",
     "debug_command_queue.cc",
     "debug_command_queue.h",
     "for_debugging_only_bindings.cc",
diff --git a/content/services/auction_worklet/auction_downloader.cc b/content/services/auction_worklet/auction_downloader.cc
index 6d36cd6..f020d80 100644
--- a/content/services/auction_worklet/auction_downloader.cc
+++ b/content/services/auction_worklet/auction_downloader.cc
@@ -159,20 +159,21 @@
           "Failed to load %s error = %s.", source_url_.spec().c_str(),
           net::ErrorToString(simple_url_loader->NetError()).c_str());
     }
-    std::move(auction_downloader_callback_).Run(nullptr /* body */, error_msg);
+    std::move(auction_downloader_callback_)
+        .Run(nullptr /* body */, nullptr /* headers */, error_msg);
   } else if (!simple_url_loader->ResponseInfo()->headers ||
              !simple_url_loader->ResponseInfo()->headers->GetNormalizedHeader(
                  "X-Allow-FLEDGE", &allow_fledge) ||
              !base::EqualsCaseInsensitiveASCII(allow_fledge, "true")) {
     std::move(auction_downloader_callback_)
-        .Run(nullptr /* body */,
+        .Run(nullptr /* body */, nullptr /* headers */,
              base::StringPrintf(
                  "Rejecting load of %s due to lack of X-Allow-FLEDGE: true.",
                  source_url_.spec().c_str()));
   } else if (!MimeTypeIsConsistent(mime_type_,
                                    simple_url_loader->ResponseInfo())) {
     std::move(auction_downloader_callback_)
-        .Run(nullptr /* body */,
+        .Run(nullptr /* body */, nullptr /* headers */,
              base::StringPrintf(
                  "Rejecting load of %s due to unexpected MIME type.",
                  source_url_.spec().c_str()));
@@ -180,14 +181,16 @@
              !IsAllowedCharset(simple_url_loader->ResponseInfo()->charset,
                                *body)) {
     std::move(auction_downloader_callback_)
-        .Run(nullptr /* body */,
+        .Run(nullptr /* body */, nullptr /* headers */,
              base::StringPrintf(
                  "Rejecting load of %s due to unexpected charset.",
                  source_url_.spec().c_str()));
   } else {
     // All OK!
     std::move(auction_downloader_callback_)
-        .Run(std::move(body), absl::nullopt /* error_msg */);
+        .Run(std::move(body),
+             std::move(simple_url_loader->ResponseInfo()->headers),
+             absl::nullopt /* error_msg */);
   }
 }
 
@@ -201,8 +204,9 @@
   simple_url_loader_.reset();
 
   std::move(auction_downloader_callback_)
-      .Run(nullptr /* body */, base::StringPrintf("Unexpected redirect on %s.",
-                                                  source_url_.spec().c_str()));
+      .Run(nullptr /* body */, nullptr /* headers */,
+           base::StringPrintf("Unexpected redirect on %s.",
+                              source_url_.spec().c_str()));
 }
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/auction_downloader.h b/content/services/auction_worklet/auction_downloader.h
index 06a4a363..73994d53 100644
--- a/content/services/auction_worklet/auction_downloader.h
+++ b/content/services/auction_worklet/auction_downloader.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "net/http/http_response_headers.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
@@ -37,6 +38,7 @@
   // invoked after the AuctionDownloader is destroyed.
   using AuctionDownloaderCallback =
       base::OnceCallback<void(std::unique_ptr<std::string> response_body,
+                              scoped_refptr<net::HttpResponseHeaders> headers,
                               absl::optional<std::string> error)>;
 
   // Starts loading the worklet script on construction. Callback will be invoked
diff --git a/content/services/auction_worklet/auction_downloader_unittest.cc b/content/services/auction_worklet/auction_downloader_unittest.cc
index c44efb8..abf38f9 100644
--- a/content/services/auction_worklet/auction_downloader_unittest.cc
+++ b/content/services/auction_worklet/auction_downloader_unittest.cc
@@ -59,10 +59,12 @@
 
  protected:
   void DownloadCompleteCallback(std::unique_ptr<std::string> body,
+                                scoped_refptr<net::HttpResponseHeaders> headers,
                                 absl::optional<std::string> error) {
     DCHECK(!body_);
     DCHECK(run_loop_);
     body_ = std::move(body);
+    headers_ = std::move(headers);
     error_ = std::move(error);
     EXPECT_EQ(error_.has_value(), !body_);
     run_loop_->Quit();
@@ -77,6 +79,7 @@
 
   std::unique_ptr<base::RunLoop> run_loop_;
   std::unique_ptr<std::string> body_;
+  scoped_refptr<net::HttpResponseHeaders> headers_;
   absl::optional<std::string> error_;
 
   network::TestURLLoaderFactory url_loader_factory_;
@@ -174,6 +177,47 @@
       last_error_msg());
 }
 
+TEST_F(AuctionDownloaderTest, PassesHeaders) {
+  std::string allow_fledge_string;
+  std::string data_version_string;
+
+  AddResponse(&url_loader_factory_, url_, kJavascriptMimeType, kUtf8Charset,
+              kAsciiResponseBody, "X-Allow-FLEDGE: true");
+  EXPECT_TRUE(RunRequest()) << last_error_msg();
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("X-Allow-FLEDGE", &allow_fledge_string));
+  EXPECT_EQ("true", allow_fledge_string);
+  EXPECT_FALSE(
+      headers_->GetNormalizedHeader("Data-Version", &data_version_string));
+
+  mime_type_ = AuctionDownloader::MimeType::kJson;
+  AddVersionedJsonResponse(&url_loader_factory_, url_, kAsciiResponseBody, 10u);
+  EXPECT_TRUE(RunRequest()) << last_error_msg();
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("X-Allow-FLEDGE", &allow_fledge_string));
+  EXPECT_EQ("true", allow_fledge_string);
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("Data-Version", &data_version_string));
+  EXPECT_EQ("10", data_version_string);
+
+  AddVersionedJsonResponse(&url_loader_factory_, url_, kAsciiResponseBody, 5u);
+  EXPECT_TRUE(RunRequest()) << last_error_msg();
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("X-Allow-FLEDGE", &allow_fledge_string));
+  EXPECT_EQ("true", allow_fledge_string);
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("Data-Version", &data_version_string));
+  EXPECT_EQ("5", data_version_string);
+
+  AddJsonResponse(&url_loader_factory_, url_, kAsciiResponseBody);
+  EXPECT_TRUE(RunRequest()) << last_error_msg();
+  EXPECT_TRUE(
+      headers_->GetNormalizedHeader("X-Allow-FLEDGE", &allow_fledge_string));
+  EXPECT_EQ("true", allow_fledge_string);
+  EXPECT_FALSE(
+      headers_->GetNormalizedHeader("Data-Version", &data_version_string));
+}
+
 // Redirect responses are treated as failures.
 TEST_F(AuctionDownloaderTest, Redirect) {
   // None of these fields actually matter for this test, but a bit strange for
diff --git a/content/services/auction_worklet/auction_v8_devtools_agent.cc b/content/services/auction_worklet/auction_v8_devtools_agent.cc
index c25ca4f0..16543e8 100644
--- a/content/services/auction_worklet/auction_v8_devtools_agent.cc
+++ b/content/services/auction_worklet/auction_v8_devtools_agent.cc
@@ -36,7 +36,7 @@
 }
 
 void AuctionV8DevToolsAgent::Connect(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent,
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
     int context_group_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   receivers_.Add(this, std::move(agent), context_group_id);
diff --git a/content/services/auction_worklet/auction_v8_devtools_agent.h b/content/services/auction_worklet/auction_v8_devtools_agent.h
index 7ab9628..64183f53 100644
--- a/content/services/auction_worklet/auction_v8_devtools_agent.h
+++ b/content/services/auction_worklet/auction_v8_devtools_agent.h
@@ -13,10 +13,10 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/unique_associated_receiver_set.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 #include "v8/include/v8-inspector.h"
@@ -71,8 +71,9 @@
   // Connects an incoming Mojo debugging connection to endpoint `agent`,
   // expecting to debug things associated in the V8Helper with
   // `context_group_id`.
-  void Connect(mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent,
-               int context_group_id);
+  void Connect(
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
+      int context_group_id);
 
   // If any session debugging `context_group_id` has an instrumentation
   // breakpoint named `name` set, asks for execution to be paused at next
@@ -124,7 +125,7 @@
   const scoped_refptr<base::SequencedTaskRunner> io_session_receiver_sequence_;
 
   // Mojo pipes connected to `this`, and context group IDs associated with them.
-  mojo::ReceiverSet<blink::mojom::DevToolsAgent, int> receivers_;
+  mojo::AssociatedReceiverSet<blink::mojom::DevToolsAgent, int> receivers_;
 
   // All AuctionV8DevToolsSession objects have their lifetime limited by their
   // pipes and `this`.
diff --git a/content/services/auction_worklet/auction_v8_helper.cc b/content/services/auction_worklet/auction_v8_helper.cc
index 1e9b1e1..1bb5d54 100644
--- a/content/services/auction_worklet/auction_v8_helper.cc
+++ b/content/services/auction_worklet/auction_v8_helper.cc
@@ -300,18 +300,7 @@
       v8::Context::New(isolate(), nullptr /* extensions */, global_template);
   auto result =
       context->Global()->Delete(context, CreateStringFromLiteral("Date"));
-
-  v8::Local<v8::ObjectTemplate> console_emulation =
-      console_.GetConsoleTemplate();
-  v8::Local<v8::Object> console_obj;
-  if (console_emulation->NewInstance(context).ToLocal(&console_obj)) {
-    result = context->Global()->Set(context, CreateStringFromLiteral("console"),
-                                    console_obj);
-    DCHECK(!result.IsNothing());
-  } else {
-    DCHECK(false);
-  }
-
+  DCHECK(!result.IsNothing());
   return context;
 }
 
@@ -489,7 +478,6 @@
 
   std::string script_name = FormatScriptName(script);
   DebugContextScope maybe_debug(inspector(), context, debug_id, script_name);
-  ScopedConsoleTarget direct_console(this, script_name, &error_out);
 
   v8::Local<v8::String> v8_function_name;
   if (!CreateUtf8String(function_name).ToLocal(&v8_function_name))
@@ -634,7 +622,7 @@
 }
 
 void AuctionV8Helper::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent,
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
     scoped_refptr<base::SequencedTaskRunner> mojo_sequence,
     const DebugId& debug_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -680,22 +668,6 @@
   return FormatValue(isolate(), script->GetScriptName());
 }
 
-AuctionV8Helper::ScopedConsoleTarget::ScopedConsoleTarget(
-    AuctionV8Helper* owner,
-    const std::string& console_script_name,
-    std::vector<std::string>* out)
-    : owner_(owner) {
-  DCHECK(!owner_->console_buffer_);
-  DCHECK(owner_->console_script_name_.empty());
-  owner_->console_buffer_ = out;
-  owner_->console_script_name_ = console_script_name;
-}
-
-AuctionV8Helper::ScopedConsoleTarget::~ScopedConsoleTarget() {
-  owner_->console_buffer_ = nullptr;
-  owner_->console_script_name_ = std::string();
-}
-
 AuctionV8Helper::AuctionV8Helper(
     scoped_refptr<base::SingleThreadTaskRunner> v8_runner)
     : base::RefCountedDeleteOnSequence<AuctionV8Helper>(v8_runner),
diff --git a/content/services/auction_worklet/auction_v8_helper.h b/content/services/auction_worklet/auction_v8_helper.h
index 5840ca9..0aeee9b 100644
--- a/content/services/auction_worklet/auction_v8_helper.h
+++ b/content/services/auction_worklet/auction_v8_helper.h
@@ -21,8 +21,8 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
-#include "content/services/auction_worklet/console.h"
 #include "gin/public/isolate_holder.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 #include "url/gurl.h"
@@ -144,8 +144,7 @@
   }
 
   // Create a v8::Context. The one thing this does that v8::Context::New() does
-  // not is remove access the Date object. It also (for now) installs some
-  // rudimentary console emulation.
+  // not is remove access to the Date object.
   v8::Local<v8::Context> CreateContext(
       v8::Local<v8::ObjectTemplate> global_template =
           v8::Local<v8::ObjectTemplate>());
@@ -234,7 +233,7 @@
   // Running this multiple times in the same context will re-load the entire
   // script file in the context, and then run the script again.
   //
-  // In case of an error or console output sets `error_out`.
+  // In case of an error sets `error_out`.
   v8::MaybeLocal<v8::Value> RunScript(v8::Local<v8::Context> context,
                                       v8::Local<v8::UnboundScript> script,
                                       const DebugId* debug_id,
@@ -252,22 +251,6 @@
 
   void set_script_timeout_for_testing(base::TimeDelta script_timeout);
 
-  // If non-nullptr, this returns a pointer to the of vector representing the
-  // debug output lines of the currently running script.  It's nullptr when
-  // nothing is running.
-  std::vector<std::string>* console_buffer() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return console_buffer_;
-  }
-
-  // Returns a string identifying the currently running script for purpose of
-  // attributing its debug output in a human-understandable way. Empty if
-  // nothing is running.
-  const std::string& console_script_name() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return console_script_name_;
-  }
-
   // Invokes the registered resume callback for given ID. Does nothing if it
   // was already invoked.
   void Resume(int context_group_id);
@@ -288,7 +271,7 @@
   // value passed in for `mojo_sequence` the first time this method is called
   // will be used.
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent,
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
       scoped_refptr<base::SequencedTaskRunner> mojo_sequence,
       const DebugId& debug_id);
 
@@ -318,20 +301,6 @@
   friend class base::DeleteHelper<AuctionV8Helper>;
   class ScriptTimeoutHelper;
 
-  // Sets values of console_buffer() and console_script_name() to those
-  // passed-in to its constructor for duration of its existence, and clears
-  // them afterward.
-  class ScopedConsoleTarget {
-   public:
-    ScopedConsoleTarget(AuctionV8Helper* owner,
-                        const std::string& console_script_name,
-                        std::vector<std::string>* out);
-    ~ScopedConsoleTarget();
-
-   private:
-    raw_ptr<AuctionV8Helper> owner_;
-  };
-
   explicit AuctionV8Helper(
       scoped_refptr<base::SingleThreadTaskRunner> v8_runner);
   ~AuctionV8Helper();
@@ -356,18 +325,12 @@
 
   std::unique_ptr<gin::IsolateHolder> isolate_holder_
       GUARDED_BY_CONTEXT(sequence_checker_);
-  Console console_ GUARDED_BY_CONTEXT(sequence_checker_){this};
   v8::Global<v8::Context> scratch_context_
       GUARDED_BY_CONTEXT(sequence_checker_);
   // Script timeout. Can be changed for testing.
   base::TimeDelta script_timeout_ GUARDED_BY_CONTEXT(sequence_checker_) =
       kScriptTimeout;
 
-  // See corresponding getters for description.
-  raw_ptr<std::vector<std::string>> console_buffer_
-      GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
-  std::string console_script_name_ GUARDED_BY_CONTEXT(sequence_checker_);
-
   raw_ptr<ScriptTimeoutHelper> timeout_helper_
       GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
 
diff --git a/content/services/auction_worklet/auction_v8_helper_unittest.cc b/content/services/auction_worklet/auction_v8_helper_unittest.cc
index 37bfbf1..cbb413e 100644
--- a/content/services/auction_worklet/auction_v8_helper_unittest.cc
+++ b/content/services/auction_worklet/auction_v8_helper_unittest.cc
@@ -16,9 +16,11 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/worklet_devtools_debug_test_util.h"
 #include "content/services/auction_worklet/worklet_v8_debug_test_util.h"
 #include "gin/converter.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -38,6 +40,78 @@
 const char kMinimalWasmModuleBytes[] = {0x00, 0x61, 0x73, 0x6d,
                                         0x01, 0x00, 0x00, 0x00};
 
+// ConnectDevToolsAgent takes an associated interface, which normally needs to
+// be passed through a different pipe to be usable.  The usual way of testing
+// this is by using BindNewEndpointAndPassDedicatedReceiver to force creation
+// of a new pipe.  Unfortunately, this doesn't appear to be compatible with how
+// our threads are setup.  So instead, we emulate how this would normally be
+// used: by call to a worklet, and just have a mock implementation that only
+// supports ConnectDevToolsAgent.
+class DebugConnector : public auction_worklet::mojom::BidderWorklet {
+ public:
+  // Expected to be run on V8 thread.
+  static void Create(
+      scoped_refptr<AuctionV8Helper> auction_v8_helper,
+      scoped_refptr<base::SequencedTaskRunner> mojo_thread,
+      scoped_refptr<AuctionV8Helper::DebugId> debug_id,
+      mojo::PendingReceiver<auction_worklet::mojom::BidderWorklet>
+          pending_receiver) {
+    DCHECK(auction_v8_helper->v8_runner()->RunsTasksInCurrentSequence());
+    auto instance = base::WrapUnique(
+        new DebugConnector(std::move(auction_v8_helper), std::move(mojo_thread),
+                           std::move(debug_id)));
+    mojo::MakeSelfOwnedReceiver(std::move(instance),
+                                std::move(pending_receiver));
+  }
+
+  void GenerateBid(
+      auction_worklet::mojom::BidderWorkletNonSharedParamsPtr
+          bidder_worklet_non_shared_params,
+      const absl::optional<std::string>& auction_signals_json,
+      const absl::optional<std::string>& per_buyer_signals_json,
+      const url::Origin& seller_origin,
+      auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
+      base::Time auction_start_time,
+      GenerateBidCallback generate_bid_callback) override {
+    ADD_FAILURE() << "GenerateBid shouldn't be called on DebugConnector";
+  }
+
+  void ReportWin(const std::string& interest_group_name,
+                 const absl::optional<std::string>& auction_signals_json,
+                 const absl::optional<std::string>& per_buyer_signals_json,
+                 const std::string& seller_signals_json,
+                 const GURL& browser_signal_render_url,
+                 double browser_signal_bid,
+                 const url::Origin& browser_signal_seller_origin,
+                 ReportWinCallback report_win_callback) override {
+    ADD_FAILURE() << "ReportWin shouldn't be called on DebugConnector";
+  }
+
+  void SendPendingSignalsRequests() override {
+    ADD_FAILURE()
+        << "SendPendingSignalsRequests shouldn't be called on DebugConnector";
+  }
+
+  void ConnectDevToolsAgent(
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent>
+          agent_receiver) override {
+    auction_v8_helper_->ConnectDevToolsAgent(std::move(agent_receiver),
+                                             mojo_thread_, *debug_id_);
+  }
+
+ private:
+  DebugConnector(scoped_refptr<AuctionV8Helper> auction_v8_helper,
+                 scoped_refptr<base::SequencedTaskRunner> mojo_thread,
+                 scoped_refptr<AuctionV8Helper::DebugId> debug_id)
+      : auction_v8_helper_(std::move(auction_v8_helper)),
+        mojo_thread_(std::move(mojo_thread)),
+        debug_id_(std::move(debug_id)) {}
+
+  scoped_refptr<AuctionV8Helper> auction_v8_helper_;
+  scoped_refptr<base::SequencedTaskRunner> mojo_thread_;
+  scoped_refptr<AuctionV8Helper::DebugId> debug_id_;
+};
+
 class AuctionV8HelperTest : public testing::Test {
  public:
   explicit AuctionV8HelperTest(
@@ -143,23 +217,20 @@
     return success;
   }
 
-  void ConnectToDevToolsAgent(
+  mojo::Remote<auction_worklet::mojom::BidderWorklet> ConnectToDevToolsAgent(
       scoped_refptr<AuctionV8Helper::DebugId> debug_id,
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent_receiver) {
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent>
+          agent_receiver) {
     DCHECK(debug_id);
+    mojo::Remote<auction_worklet::mojom::BidderWorklet> connector_pipe;
+
     helper_->v8_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](scoped_refptr<AuctionV8Helper> helper,
-               mojo::PendingReceiver<blink::mojom::DevToolsAgent>
-                   agent_receiver,
-               scoped_refptr<base::SequencedTaskRunner> mojo_thread,
-               scoped_refptr<AuctionV8Helper::DebugId> debug_id) {
-              helper->ConnectDevToolsAgent(std::move(agent_receiver),
-                                           std::move(mojo_thread), *debug_id);
-            },
-            helper_, std::move(agent_receiver),
-            base::SequencedTaskRunnerHandle::Get(), std::move(debug_id)));
+        FROM_HERE, base::BindOnce(&DebugConnector::Create, helper_,
+                                  base::SequencedTaskRunnerHandle::Get(),
+                                  std::move(debug_id),
+                                  connector_pipe.BindNewPipeAndPassReceiver()));
+    connector_pipe->ConnectDevToolsAgent(std::move(agent_receiver));
+    return connector_pipe;
   }
 
  protected:
@@ -414,49 +485,107 @@
   EXPECT_THAT(error_msgs[0], HasSubstr("notfound"));
 }
 
-TEST_F(AuctionV8HelperTest, LogThenError) {
+TEST_F(AuctionV8HelperTest, ConsoleLog) {
+  // Console log output is reported by V8 via debugging channels.
+  // Need to use a separate thread for debugger stuff.
+  v8_scope_.reset();
+  helper_ = AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner());
+  ScopedInspectorSupport inspector_support(helper_.get());
+
+  auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
+  TestChannel* channel =
+      inspector_support.ConnectDebuggerSession(id->context_group_id());
+  channel->RunCommandAndWaitForResult(
+      1, "Runtime.enable", R"({"id":1,"method":"Runtime.enable","params":{}})");
+  channel->RunCommandAndWaitForResult(
+      2, "Debugger.enable",
+      R"({"id":2,"method":"Debugger.enable","params":{}})");
+
   const char kScript[] = R"(
     console.debug('debug is there');
-    console.error('error is also there');
 
     function foo() {
-      console.info('info too');
-      console.log('can', 'log', 'multiple', 'things');
-      console.warn('conversions?', true);
-      console.table('not the fancier stuff, though');
+      console.log('can', 'log', 'multiple', 'things', true);
+      console.table('even table!');
     }
   )";
 
-  v8::Local<v8::UnboundScript> script;
+  base::RunLoop run_loop;
+  CompileAndRunScriptOnV8Thread(id, "foo", GURL("https://foo.test/"), kScript,
+                                /*expect_success=*/true,
+                                run_loop.QuitClosure());
+  run_loop.Run();
+
   {
-    v8::Context::Scope ctx(helper_->scratch_context());
-    absl::optional<std::string> error_msg;
-    ASSERT_TRUE(helper_
-                    ->Compile(kScript, GURL("https://foo.test/"),
-                              /*debug_id=*/nullptr, error_msg)
-                    .ToLocal(&script));
-    EXPECT_FALSE(error_msg.has_value());
+    TestChannel::Event message =
+        channel->WaitForMethodNotification("Runtime.consoleAPICalled");
+    const std::string* type = message.value.FindStringPath("params.type");
+    ASSERT_TRUE(type);
+    EXPECT_EQ("debug", *type);
+    const base::Value* args = message.value.FindListPath("params.args");
+    ASSERT_TRUE(args);
+    ASSERT_EQ(1u, args->GetList().size());
+    EXPECT_EQ("string", *args->GetList()[0].FindStringKey("type"));
+    EXPECT_EQ("debug is there", *args->GetList()[0].FindStringKey("value"));
+    const base::Value* stack_trace =
+        message.value.FindListPath("params.stackTrace.callFrames");
+    ASSERT_EQ(1u, stack_trace->GetList().size());
+    EXPECT_EQ("", *stack_trace->GetList()[0].FindStringKey("functionName"));
+    EXPECT_EQ("https://foo.test/",
+              *stack_trace->GetList()[0].FindStringKey("url"));
+    EXPECT_EQ(1, *stack_trace->GetList()[0].FindIntKey("lineNumber"));
   }
 
-  v8::Local<v8::Context> context = helper_->CreateContext();
+  {
+    TestChannel::Event message =
+        channel->WaitForMethodNotification("Runtime.consoleAPICalled");
+    const std::string* type = message.value.FindStringPath("params.type");
+    ASSERT_TRUE(type);
+    EXPECT_EQ("log", *type);
+    const base::Value* args = message.value.FindListPath("params.args");
+    ASSERT_TRUE(args);
+    ASSERT_EQ(5u, args->GetList().size());
+    EXPECT_EQ("string", *args->GetList()[0].FindStringKey("type"));
+    EXPECT_EQ("can", *args->GetList()[0].FindStringKey("value"));
+    EXPECT_EQ("string", *args->GetList()[1].FindStringKey("type"));
+    EXPECT_EQ("log", *args->GetList()[1].FindStringKey("value"));
+    EXPECT_EQ("string", *args->GetList()[2].FindStringKey("type"));
+    EXPECT_EQ("multiple", *args->GetList()[2].FindStringKey("value"));
+    EXPECT_EQ("string", *args->GetList()[3].FindStringKey("type"));
+    EXPECT_EQ("things", *args->GetList()[3].FindStringKey("value"));
+    EXPECT_EQ("boolean", *args->GetList()[4].FindStringKey("type"));
+    EXPECT_EQ(true, *args->GetList()[4].FindBoolKey("value"));
 
-  std::vector<std::string> error_msgs;
-  v8::Context::Scope ctx(context);
-  v8::Local<v8::Value> result;
-  ASSERT_FALSE(helper_
-                   ->RunScript(context, script,
-                               /*debug_id=*/nullptr, "foo",
-                               base::span<v8::Local<v8::Value>>(), error_msgs)
-                   .ToLocal(&result));
-  ASSERT_EQ(error_msgs.size(), 6u);
-  EXPECT_EQ("https://foo.test/ [Debug]: debug is there", error_msgs[0]);
-  EXPECT_EQ("https://foo.test/ [Error]: error is also there", error_msgs[1]);
-  EXPECT_EQ("https://foo.test/ [Info]: info too", error_msgs[2]);
-  EXPECT_EQ("https://foo.test/ [Log]: can log multiple things", error_msgs[3]);
-  EXPECT_EQ("https://foo.test/ [Warn]: conversions? true", error_msgs[4]);
-  EXPECT_THAT(error_msgs[5], StartsWith("https://foo.test/:9"));
-  EXPECT_THAT(error_msgs[5], HasSubstr("TypeError"));
-  EXPECT_THAT(error_msgs[5], HasSubstr("table"));
+    const base::Value* stack_trace =
+        message.value.FindListPath("params.stackTrace.callFrames");
+    ASSERT_EQ(1u, stack_trace->GetList().size());
+    EXPECT_EQ("foo", *stack_trace->GetList()[0].FindStringKey("functionName"));
+    EXPECT_EQ("https://foo.test/",
+              *stack_trace->GetList()[0].FindStringKey("url"));
+    EXPECT_EQ(4, *stack_trace->GetList()[0].FindIntKey("lineNumber"));
+  }
+
+  {
+    TestChannel::Event message =
+        channel->WaitForMethodNotification("Runtime.consoleAPICalled");
+    const std::string* type = message.value.FindStringPath("params.type");
+    ASSERT_TRUE(type);
+    EXPECT_EQ("table", *type);
+    const base::Value* args = message.value.FindListPath("params.args");
+    ASSERT_TRUE(args);
+    ASSERT_EQ(1u, args->GetList().size());
+    EXPECT_EQ("string", *args->GetList()[0].FindStringKey("type"));
+    EXPECT_EQ("even table!", *args->GetList()[0].FindStringKey("value"));
+    const base::Value* stack_trace =
+        message.value.FindListPath("params.stackTrace.callFrames");
+    ASSERT_EQ(1u, stack_trace->GetList().size());
+    EXPECT_EQ("foo", *stack_trace->GetList()[0].FindStringKey("functionName"));
+    EXPECT_EQ("https://foo.test/",
+              *stack_trace->GetList()[0].FindStringKey("url"));
+    EXPECT_EQ(5, *stack_trace->GetList()[0].FindIntKey("lineNumber"));
+  }
+
+  id->AbortDebuggerPauses();
 }
 
 TEST_F(AuctionV8HelperTest, FormatScriptName) {
@@ -681,8 +810,9 @@
 
     auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
 
-    mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-    ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+    mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+    auto connector = ConnectToDevToolsAgent(
+        id, agent_remote.BindNewEndpointAndPassReceiver());
 
     TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                          use_binary_protocol);
@@ -791,8 +921,9 @@
 
       auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
 
-      mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-      ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+      mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+      auto connector = ConnectToDevToolsAgent(
+          id, agent_remote.BindNewEndpointAndPassReceiver());
 
       TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                            use_binary_protocol);
@@ -903,8 +1034,9 @@
 
     auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
 
-    mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-    ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+    mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+    auto connector = ConnectToDevToolsAgent(
+        id, agent_remote.BindNewEndpointAndPassReceiver());
 
     TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                          use_binary_protocol);
@@ -928,8 +1060,9 @@
 
   auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-  ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+  auto connector =
+      ConnectToDevToolsAgent(id, agent_remote.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                        use_binary_protocol);
@@ -975,8 +1108,9 @@
   helper_ = AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner());
 
   auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
-  mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-  ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+  auto connector =
+      ConnectToDevToolsAgent(id, agent_remote.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                        /*use_binary_protocol=*/true);
@@ -1043,8 +1177,9 @@
   helper_ = AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner());
 
   auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
-  mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-  ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+  auto connector =
+      ConnectToDevToolsAgent(id, agent_remote.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                        /*use_binary_protocol=*/false);
@@ -1129,8 +1264,9 @@
 
   auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
-  ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_remote;
+  auto connector =
+      ConnectToDevToolsAgent(id, agent_remote.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
                                        /*use_binary_protocol=*/false);
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index c82c902..dd1fc5a 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -270,7 +270,7 @@
 }
 
 void BidderWorklet::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) {
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
   v8_runner_->PostTask(
       FROM_HERE,
@@ -647,7 +647,7 @@
 }
 
 void BidderWorklet::V8State::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) {
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   v8_helper_->ConnectDevToolsAgent(std::move(agent), user_thread_, *debug_id_);
 }
diff --git a/content/services/auction_worklet/bidder_worklet.h b/content/services/auction_worklet/bidder_worklet.h
index f701b526..a2fe9f80 100644
--- a/content/services/auction_worklet/bidder_worklet.h
+++ b/content/services/auction_worklet/bidder_worklet.h
@@ -21,6 +21,7 @@
 #include "content/services/auction_worklet/trusted_signals.h"
 #include "content/services/auction_worklet/trusted_signals_request_manager.h"
 #include "content/services/auction_worklet/worklet_loader.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/struct_ptr.h"
@@ -108,7 +109,8 @@
                  const url::Origin& browser_signal_seller_origin,
                  ReportWinCallback report_win_callback) override;
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override;
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override;
 
  private:
   struct GenerateBidTask {
@@ -199,7 +201,7 @@
         GenerateBidCallbackInternal callback);
 
     void ConnectDevToolsAgent(
-        mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent);
+        mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent);
 
    private:
     friend class base::DeleteHelper<V8State>;
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 881f18b..8a5e7d3 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -942,20 +942,6 @@
       {"https://url.test/:5 Uncaught ReferenceError: Date is not defined."});
 }
 
-TEST_F(BidderWorkletTest, GenerateBidLogAndError) {
-  const char kScript[] = R"(
-    function generateBid() {
-      console.log("Logging");
-      return "hello";
-    }
-  )";
-
-  RunGenerateBidWithJavascriptExpectingResult(
-      kScript, mojom::BidderWorkletBidPtr() /* expected_bid */,
-      {"https://url.test/ [Log]: Logging",
-       "https://url.test/ generateBid() return value not an object."});
-}
-
 TEST_F(BidderWorkletTest, GenerateBidInterestGroupName) {
   const std::string kGenerateBidBody =
       R"({ad: interestGroup.name, bid:1, render:"https://response.test/"})";
@@ -2531,9 +2517,9 @@
       CreateWorklet(GURL(kUrl2), true /* pause_for_debugger_on_start */);
   GenerateBid(worklet2.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent1, agent2;
-  worklet1->ConnectDevToolsAgent(agent1.BindNewPipeAndPassReceiver());
-  worklet2->ConnectDevToolsAgent(agent2.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent1, agent2;
+  worklet1->ConnectDevToolsAgent(agent1.BindNewEndpointAndPassReceiver());
+  worklet2->ConnectDevToolsAgent(agent2.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug1(std::move(agent1), "123",
                                  true /* use_binary_protocol */);
@@ -2666,8 +2652,8 @@
       CreateWorklet(GURL(kUrl), true /* pause_for_debugger_on_start */);
   GenerateBid(worklet.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent;
-  worklet->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
+  worklet->ConnectDevToolsAgent(agent.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug(std::move(agent), "123",
                                 true /* use_binary_protocol */);
@@ -2748,8 +2734,8 @@
       CreateWorklet(GURL(kUrl), /*pause_for_debugger_on_start=*/true);
   GenerateBid(worklet.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent;
-  worklet->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
+  worklet->ConnectDevToolsAgent(agent.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug(std::move(agent), "123",
                                 /*use_binary_protocol=*/true);
diff --git a/content/services/auction_worklet/console.cc b/content/services/auction_worklet/console.cc
deleted file mode 100644
index 5586c8a..0000000
--- a/content/services/auction_worklet/console.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2021 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.
-
-#include "content/services/auction_worklet/console.h"
-
-#include <string>
-#include <utility>
-
-#include "base/strings/strcat.h"
-#include "content/services/auction_worklet/auction_v8_helper.h"
-#include "v8/include/v8-external.h"
-#include "v8/include/v8-template.h"
-
-namespace auction_worklet {
-
-Console::Console(AuctionV8Helper* v8_helper) : v8_helper_(v8_helper) {}
-Console::~Console() = default;
-
-v8::Local<v8::ObjectTemplate> Console::GetConsoleTemplate() {
-  v8::Local<v8::ObjectTemplate> console_template =
-      v8::ObjectTemplate::New(v8_helper_->isolate());
-
-  v8::Local<v8::External> v8_this =
-      v8::External::New(v8_helper_->isolate(), this);
-
-  RegisterConsoleMethod(v8_this, "debug", &Console::ConsoleDebug,
-                        console_template);
-  RegisterConsoleMethod(v8_this, "error", &Console::ConsoleError,
-                        console_template);
-  RegisterConsoleMethod(v8_this, "info", &Console::ConsoleInfo,
-                        console_template);
-  RegisterConsoleMethod(v8_this, "log", &Console::ConsoleLog, console_template);
-  RegisterConsoleMethod(v8_this, "warn", &Console::ConsoleWarn,
-                        console_template);
-
-  return console_template;
-}
-
-void Console::RegisterConsoleMethod(
-    v8::Local<v8::External> v8_this,
-    const char* name,
-    ConsoleFn function,
-    v8::Local<v8::ObjectTemplate> console_template) {
-  v8::Local<v8::FunctionTemplate> function_obj =
-      v8::FunctionTemplate::New(v8_helper_->isolate(), function, v8_this);
-  function_obj->RemovePrototype();
-  console_template->Set(v8_helper_->CreateStringFromLiteral(name),
-                        function_obj);
-}
-
-// static
-void Console::ConsoleDebug(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  Console* console =
-      static_cast<Console*>(v8::External::Cast(*args.Data())->Value());
-  console->DoConsoleOut("[Debug]", args);
-}
-
-// static
-void Console::ConsoleError(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  Console* console =
-      static_cast<Console*>(v8::External::Cast(*args.Data())->Value());
-  console->DoConsoleOut("[Error]", args);
-}
-
-// static
-void Console::ConsoleInfo(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  Console* console =
-      static_cast<Console*>(v8::External::Cast(*args.Data())->Value());
-  console->DoConsoleOut("[Info]", args);
-}
-
-// static
-void Console::ConsoleLog(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  Console* console =
-      static_cast<Console*>(v8::External::Cast(*args.Data())->Value());
-  console->DoConsoleOut("[Log]", args);
-}
-
-// static
-void Console::ConsoleWarn(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  Console* console =
-      static_cast<Console*>(v8::External::Cast(*args.Data())->Value());
-  console->DoConsoleOut("[Warn]", args);
-}
-
-void Console::DoConsoleOut(const std::string& prefix,
-                           const v8::FunctionCallbackInfo<v8::Value>& args) {
-  if (!v8_helper_->console_buffer())
-    return;
-
-  std::string result =
-      base::StrCat({v8_helper_->console_script_name(), " ", prefix, ": "});
-  for (int i = 0; i < args.Length(); ++i) {
-    v8::String::Utf8Value val_utf8(v8_helper_->isolate(), args[i]);
-    if (i != 0)
-      result += ' ';
-    if (*val_utf8)
-      result += std::string(*val_utf8, val_utf8.length());
-  }
-
-  v8_helper_->console_buffer()->push_back(std::move(result));
-}
-
-}  // namespace auction_worklet
diff --git a/content/services/auction_worklet/console.h b/content/services/auction_worklet/console.h
deleted file mode 100644
index 74e9408a..0000000
--- a/content/services/auction_worklet/console.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2021 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.
-
-#ifndef CONTENT_SERVICES_AUCTION_WORKLET_CONSOLE_H_
-#define CONTENT_SERVICES_AUCTION_WORKLET_CONSOLE_H_
-
-#include <string>
-
-#include "base/memory/raw_ptr.h"
-#include "v8/include/v8-forward.h"
-
-namespace auction_worklet {
-
-class AuctionV8Helper;
-
-// A basic interim implementation of console output. This doesn't support
-// format specifiers.  Is owned by an AuctionV8Helper (keeping a back pointer),
-// and writes to AuctionV8Helper::console_buffer() active at time of any JS call
-// it handles.
-class Console {
- public:
-  Console(const Console&) = delete;
-  Console& operator=(const Console&) = delete;
-  ~Console();
-
-  // Returns an object template that provides a minimal replacement for the
-  // standard console object. Refers to `this`, so should not outlast it.
-  v8::Local<v8::ObjectTemplate> GetConsoleTemplate();
-
- private:
-  friend class AuctionV8Helper;
-  typedef void(ConsoleFn)(const v8::FunctionCallbackInfo<v8::Value>&);
-
-  // Should not outlast `v8_helper` (which is the only thing that should create
-  // this in the first place).
-  explicit Console(AuctionV8Helper* v8_helper);
-
-  void RegisterConsoleMethod(v8::Local<v8::External> v8_this,
-                             const char* name,
-                             ConsoleFn function,
-                             v8::Local<v8::ObjectTemplate> console_template);
-
-  static void ConsoleDebug(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void ConsoleError(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void ConsoleInfo(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void ConsoleLog(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void ConsoleWarn(const v8::FunctionCallbackInfo<v8::Value>& args);
-
-  void DoConsoleOut(const std::string& prefix,
-                    const v8::FunctionCallbackInfo<v8::Value>& args);
-
-  const raw_ptr<AuctionV8Helper> v8_helper_;
-};
-
-}  // namespace auction_worklet
-
-#endif  // CONTENT_SERVICES_AUCTION_WORKLET_CONSOLE_H_
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 96b5c02..b730656 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -208,5 +208,6 @@
                 array<string> errors);
 
   // Establishes a debugger connection to the worklet.
-  ConnectDevToolsAgent(pending_receiver<blink.mojom.DevToolsAgent> agent);
+  ConnectDevToolsAgent(
+      pending_associated_receiver<blink.mojom.DevToolsAgent> agent);
 };
diff --git a/content/services/auction_worklet/public/mojom/seller_worklet.mojom b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
index 9a903b72..96a8117 100644
--- a/content/services/auction_worklet/public/mojom/seller_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
@@ -55,6 +55,13 @@
   //  of 0 indicates either an error running the script, or that the script
   //  indicated the bid should not be used.
   //
+  // `data_version` The value of the Data-Version header served with the
+  //  trusted scoring signals.
+  //
+  // `has_data_version` True to indicate the Data-Version header was present in
+  //  the HTTP response for the trusted scoring signals, false otherwise.
+  //  TODO(https://crbug.com/657632): Update when optional integers supported.
+  //
   // `debug_loss_report_url` The URL to request if this bid does not win the
   // auction. It's requested if the auction runs to completion and this is not
   // the winning bid, including the case that this worklet rejects this bid
@@ -80,6 +87,8 @@
           array<url.mojom.Url> browser_signal_ad_component_render_urls,
           uint32 browser_signal_bidding_duration_msecs) =>
               (double score,
+              uint32 data_version,
+              bool has_data_version,
               url.mojom.Url? debug_loss_report_url,
               url.mojom.Url? debug_win_report_url,
               array<string> errors);
@@ -115,6 +124,13 @@
   // `browser_signal_desirability` The score returned by ScoreAd for the
   //  the winning bid.
   //
+  // `browser_signal_data_version` The value of the Data-Version header served
+  //  with the trusted scoring signals.
+  //
+  // `browser_signal_has_data_version` True to indicate Data-Version header was
+  //  present in the HTTP response for the trusted scoring signals.
+  //  TODO(https://crbug.com/657632): Update when optional integers supported.
+  //
   // Returns:
   // `signals_for_winner` The value to pass to the winning bidder's
   //  ReportWin function, as a JSON string. Null if no value is provided.
@@ -132,11 +148,14 @@
       url.mojom.Origin browser_signal_interest_group_owner,
       url.mojom.Url browser_signal_render_url,
       double browser_signal_bid,
-      double browser_signal_desirability) =>
+      double browser_signal_desirability,
+      uint32 browser_signal_data_version,
+      bool browser_signal_has_data_version) =>
           (string? signals_for_winner,
            url.mojom.Url? report_url,
            array<string> error_msgs);
 
   // Establishes a debugger connection to the worklet.
-  ConnectDevToolsAgent(pending_receiver<blink.mojom.DevToolsAgent> agent);
+  ConnectDevToolsAgent(
+      pending_associated_receiver<blink.mojom.DevToolsAgent> agent);
 };
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index 124bdbf..3773721 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -219,6 +219,8 @@
     const GURL& browser_signal_render_url,
     double browser_signal_bid,
     double browser_signal_desirability,
+    uint32_t browser_signal_data_version,
+    bool browser_signal_has_data_version,
     ReportResultCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
@@ -233,6 +235,9 @@
   report_result_task->browser_signal_render_url = browser_signal_render_url;
   report_result_task->browser_signal_bid = browser_signal_bid;
   report_result_task->browser_signal_desirability = browser_signal_desirability;
+  if (browser_signal_has_data_version)
+    report_result_task->browser_signal_data_version =
+        browser_signal_data_version;
   report_result_task->callback = std::move(callback);
 
   // If not yet ready, need to wait for load to complete.
@@ -243,7 +248,7 @@
 }
 
 void SellerWorklet::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) {
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
   v8_runner_->PostTask(
       FROM_HERE,
@@ -307,6 +312,7 @@
   std::vector<v8::Local<v8::Value>> args;
   if (!v8_helper_->AppendJsonValue(context, ad_metadata_json, &args)) {
     PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                    /*data_version=*/absl::nullopt,
                                     /*debug_loss_report_url=*/absl::nullopt,
                                     /*debug_win_report_url=*/absl::nullopt,
                                     /*errors=*/std::vector<std::string>());
@@ -318,6 +324,7 @@
   if (!AppendAuctionConfig(v8_helper_.get(), context, decision_logic_url_,
                            *auction_ad_config_non_shared_params, &args)) {
     PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                    /*data_version=*/absl::nullopt,
                                     /*debug_loss_report_url=*/absl::nullopt,
                                     /*debug_win_report_url=*/absl::nullopt,
                                     /*errors=*/std::vector<std::string>());
@@ -325,10 +332,12 @@
   }
 
   v8::Local<v8::Value> trusted_scoring_signals_value;
+  absl::optional<uint32_t> data_version;
   if (trusted_scoring_signals) {
     trusted_scoring_signals_value = trusted_scoring_signals->GetScoringSignals(
         v8_helper_.get(), context, browser_signal_render_url,
         browser_signal_ad_components);
+    data_version = trusted_scoring_signals->GetDataVersion();
   } else {
     trusted_scoring_signals_value = v8::Null(isolate);
   }
@@ -346,6 +355,7 @@
       !browser_signals_dict.Set("biddingDurationMsec",
                                 browser_signal_bidding_duration_msecs)) {
     PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                    /*data_version=*/absl::nullopt,
                                     /*debug_loss_report_url=*/absl::nullopt,
                                     /*debug_win_report_url=*/absl::nullopt,
                                     /*errors=*/std::vector<std::string>());
@@ -355,6 +365,7 @@
     if (!browser_signals_dict.Set("adComponents",
                                   browser_signal_ad_components)) {
       PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                      /*data_version=*/absl::nullopt,
                                       /*debug_loss_report_url=*/absl::nullopt,
                                       /*debug_win_report_url=*/absl::nullopt,
                                       /*errors=*/std::vector<std::string>());
@@ -373,6 +384,7 @@
                        "scoreAd", args, errors_out)
            .ToLocal(&score_ad_result)) {
     PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                    /*data_version=*/absl::nullopt,
                                     /*debug_loss_report_url=*/absl::nullopt,
                                     /*debug_win_report_url=*/absl::nullopt,
                                     std::move(errors_out));
@@ -386,6 +398,7 @@
                       " scoreAd() did not return a valid number."}));
 
     PostScoreAdCallbackToUserThread(std::move(callback), /*score=*/0,
+                                    /*data_version=*/absl::nullopt,
                                     /*debug_loss_report_url=*/absl::nullopt,
                                     /*debug_win_report_url=*/absl::nullopt,
                                     std::move(errors_out));
@@ -396,14 +409,14 @@
     // Keep debug report URLs because we want to send debug loss reports if
     // seller rejected all bids.
     PostScoreAdCallbackToUserThread(
-        std::move(callback), /*score=*/0,
+        std::move(callback), /*score=*/0, data_version,
         for_debugging_only_bindings.TakeLossReportUrl(),
         for_debugging_only_bindings.TakeWinReportUrl(), std::move(errors_out));
     return;
   }
 
   PostScoreAdCallbackToUserThread(
-      std::move(callback), score,
+      std::move(callback), score, data_version,
       for_debugging_only_bindings.TakeLossReportUrl(),
       for_debugging_only_bindings.TakeWinReportUrl(), std::move(errors_out));
 }
@@ -415,6 +428,7 @@
     const GURL& browser_signal_render_url,
     double browser_signal_bid,
     double browser_signal_desirability,
+    absl::optional<uint32_t> browser_signal_data_version,
     ReportResultCallbackInternal callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_.get());
@@ -449,7 +463,10 @@
       !browser_signals_dict.Set("renderUrl",
                                 browser_signal_render_url.spec()) ||
       !browser_signals_dict.Set("bid", browser_signal_bid) ||
-      !browser_signals_dict.Set("desirability", browser_signal_desirability)) {
+      !browser_signals_dict.Set("desirability", browser_signal_desirability) ||
+      (browser_signal_data_version.has_value() &&
+       !browser_signals_dict.Set("dataVersion",
+                                 browser_signal_data_version.value()))) {
     PostReportResultCallbackToUserThread(std::move(callback),
                                          /*signals_for_winner=*/absl::nullopt,
                                          /*report_url=*/absl::nullopt,
@@ -486,7 +503,7 @@
 }
 
 void SellerWorklet::V8State::ConnectDevToolsAgent(
-    mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) {
+    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   v8_helper_->ConnectDevToolsAgent(std::move(agent), user_thread_, *debug_id_);
 }
@@ -515,13 +532,14 @@
 void SellerWorklet::V8State::PostScoreAdCallbackToUserThread(
     ScoreAdCallbackInternal callback,
     double score,
+    absl::optional<uint32_t> data_version,
     absl::optional<GURL> debug_loss_report_url,
     absl::optional<GURL> debug_win_report_url,
     std::vector<std::string> errors) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   user_thread_->PostTask(
       FROM_HERE,
-      base::BindOnce(std::move(callback), score,
+      base::BindOnce(std::move(callback), score, data_version,
                      std::move(debug_loss_report_url),
                      std::move(debug_win_report_url), std::move(errors)));
 }
@@ -629,6 +647,7 @@
 void SellerWorklet::DeliverScoreAdCallbackOnUserThread(
     ScoreAdTaskList::iterator task,
     double score,
+    absl::optional<uint32_t> data_version,
     absl::optional<GURL> debug_loss_report_url,
     absl::optional<GURL> debug_win_report_url,
     std::vector<std::string> errors) {
@@ -640,7 +659,8 @@
     errors.insert(errors.begin(), *task->trusted_scoring_signals_error_msg);
 
   std::move(task->callback)
-      .Run(score, debug_loss_report_url, debug_win_report_url, errors);
+      .Run(score, data_version.value_or(0), data_version.has_value(),
+           debug_loss_report_url, debug_win_report_url, errors);
   score_ad_tasks_.erase(task);
 }
 
@@ -655,7 +675,7 @@
           std::move(task->auction_ad_config_non_shared_params),
           std::move(task->browser_signal_interest_group_owner),
           std::move(task->browser_signal_render_url), task->browser_signal_bid,
-          task->browser_signal_desirability,
+          task->browser_signal_desirability, task->browser_signal_data_version,
           base::BindOnce(
               &SellerWorklet::DeliverReportResultCallbackOnUserThread,
               weak_ptr_factory_.GetWeakPtr(), task)));
diff --git a/content/services/auction_worklet/seller_worklet.h b/content/services/auction_worklet/seller_worklet.h
index ffd1f522..2e1ed72 100644
--- a/content/services/auction_worklet/seller_worklet.h
+++ b/content/services/auction_worklet/seller_worklet.h
@@ -22,6 +22,7 @@
 #include "content/services/auction_worklet/trusted_signals.h"
 #include "content/services/auction_worklet/trusted_signals_request_manager.h"
 #include "content/services/auction_worklet/worklet_loader.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -89,9 +90,12 @@
                     const GURL& browser_signal_render_url,
                     double browser_signal_bid,
                     double browser_signal_desirability,
+                    uint32_t browser_signal_data_version,
+                    bool browser_signal_has_data_version,
                     ReportResultCallback callback) override;
   void ConnectDevToolsAgent(
-      mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override;
+      mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent)
+      override;
 
  private:
   // Contains all data needed for a ScoreAd() call. Destroyed only when its
@@ -143,6 +147,7 @@
     GURL browser_signal_render_url;
     double browser_signal_bid;
     double browser_signal_desirability;
+    absl::optional<uint32_t> browser_signal_data_version;
 
     ReportResultCallback callback;
   };
@@ -159,6 +164,7 @@
     // V8State, and avoids having to make a copy of the errors vector.
     using ScoreAdCallbackInternal =
         base::OnceCallback<void(double score,
+                                absl::optional<uint32_t> data_version,
                                 absl::optional<GURL> debug_loss_report_url,
                                 absl::optional<GURL> debug_win_report_url,
                                 std::vector<std::string> errors)>;
@@ -192,10 +198,11 @@
                       const GURL& browser_signal_render_url,
                       double browser_signal_bid,
                       double browser_signal_desirability,
+                      absl::optional<uint32_t> browser_signal_data_version,
                       ReportResultCallbackInternal callback);
 
     void ConnectDevToolsAgent(
-        mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent);
+        mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent);
 
    private:
     friend class base::DeleteHelper<V8State>;
@@ -206,6 +213,7 @@
     void PostScoreAdCallbackToUserThread(
         ScoreAdCallbackInternal callback,
         double score,
+        absl::optional<uint32_t> data_version,
         absl::optional<GURL> debug_loss_report_url,
         absl::optional<GURL> debug_win_report_url,
         std::vector<std::string> errors);
@@ -257,6 +265,7 @@
   void DeliverScoreAdCallbackOnUserThread(
       ScoreAdTaskList::iterator task,
       double score,
+      absl::optional<uint32_t> data_version,
       absl::optional<GURL> debug_loss_report_url,
       absl::optional<GURL> debug_win_report_url,
       std::vector<std::string> errors);
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 07d7bebe..da0ba86 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -150,13 +150,15 @@
       double expected_score,
       const std::vector<std::string>& expected_errors =
           std::vector<std::string>(),
+      absl::optional<uint32_t> expected_data_version = {},
       const absl::optional<GURL>& expected_debug_loss_report_url =
           absl::nullopt,
       const absl::optional<GURL>& expected_debug_win_report_url =
           absl::nullopt) {
     RunScoreAdWithJavascriptExpectingResult(
         CreateScoreAdScript(raw_return_value), expected_score, expected_errors,
-        expected_debug_loss_report_url, expected_debug_win_report_url);
+        expected_data_version, expected_debug_loss_report_url,
+        expected_debug_win_report_url);
   }
 
   // Behaves just like RunScoreAdWithReturnValueExpectingResult(), but
@@ -170,8 +172,8 @@
       const std::string& raw_return_value,
       double expected_score,
       base::TimeDelta expected_duration,
-      const std::vector<std::string>& expected_errors =
-          std::vector<std::string>(),
+      const std::vector<std::string>& expected_errors = {},
+      absl::optional<uint32_t> expected_data_version = {},
       const absl::optional<GURL>& expected_debug_loss_report_url =
           absl::nullopt,
       const absl::optional<GURL>& expected_debug_win_report_url =
@@ -181,10 +183,10 @@
     auto seller_worklet = CreateWorklet();
 
     base::RunLoop run_loop;
-    RunScoreAdOnWorkletAsync(seller_worklet.get(), expected_score,
-                             expected_errors, expected_debug_loss_report_url,
-                             expected_debug_win_report_url,
-                             run_loop.QuitClosure());
+    RunScoreAdOnWorkletAsync(
+        seller_worklet.get(), expected_score, expected_errors,
+        expected_data_version, expected_debug_loss_report_url,
+        expected_debug_win_report_url, run_loop.QuitClosure());
     task_environment_.FastForwardBy(expected_duration - kTinyTime);
     EXPECT_FALSE(run_loop.AnyQuitCalled());
     task_environment_.FastForwardBy(kTinyTime);
@@ -199,6 +201,7 @@
       double expected_score,
       const std::vector<std::string>& expected_errors =
           std::vector<std::string>(),
+      absl::optional<uint32_t> expected_data_version = {},
       const absl::optional<GURL>& expected_debug_loss_report_url =
           absl::nullopt,
       const absl::optional<GURL>& expected_debug_win_report_url =
@@ -206,9 +209,9 @@
     SCOPED_TRACE(javascript);
     AddJavascriptResponse(&url_loader_factory_, decision_logic_url_,
                           javascript);
-    RunScoreAdExpectingResult(expected_score, expected_errors,
-                              expected_debug_loss_report_url,
-                              expected_debug_win_report_url);
+    RunScoreAdExpectingResult(
+        expected_score, expected_errors, expected_data_version,
+        expected_debug_loss_report_url, expected_debug_win_report_url);
   }
 
   // Runs score_ad() script, checking result and invoking provided closure
@@ -217,6 +220,7 @@
       mojom::SellerWorklet* seller_worklet,
       double expected_score,
       const std::vector<std::string>& expected_errors,
+      absl::optional<uint32_t> expected_data_version,
       const absl::optional<GURL>& expected_debug_loss_report_url,
       const absl::optional<GURL>& expected_debug_win_report_url,
       base::OnceClosure done_closure) {
@@ -226,22 +230,28 @@
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
         base::BindOnce(
             [](double expected_score,
+               absl::optional<uint32_t> expected_data_version,
                const absl::optional<GURL>& expected_debug_loss_report_url,
                const absl::optional<GURL>& expected_debug_win_report_url,
                std::vector<std::string> expected_errors,
                base::OnceClosure done_closure, double score,
+               uint32_t data_version, bool has_data_version,
                const absl::optional<GURL>& debug_loss_report_url,
                const absl::optional<GURL>& debug_win_report_url,
                const std::vector<std::string>& errors) {
+              absl::optional<uint32_t> maybe_data_version;
+              if (has_data_version)
+                maybe_data_version = data_version;
               EXPECT_EQ(expected_score, score);
               EXPECT_EQ(expected_debug_loss_report_url, debug_loss_report_url);
               EXPECT_EQ(expected_debug_win_report_url, debug_win_report_url);
+              EXPECT_EQ(expected_data_version, maybe_data_version);
               EXPECT_EQ(expected_errors, errors);
               std::move(done_closure).Run();
             },
-            expected_score, expected_debug_loss_report_url,
-            expected_debug_win_report_url, expected_errors,
-            std::move(done_closure)));
+            expected_score, expected_data_version,
+            expected_debug_loss_report_url, expected_debug_win_report_url,
+            expected_errors, std::move(done_closure)));
   }
 
   void RunScoreAdOnWorkletExpectingCallbackNeverInvoked(
@@ -250,7 +260,8 @@
         ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
-        base::BindOnce([](double score,
+        base::BindOnce([](double score, uint32_t data_version,
+                          bool has_data_version,
                           const absl::optional<GURL>& debug_loss_report_url,
                           const absl::optional<GURL>& debug_win_report_url,
                           const std::vector<std::string>& errors) {
@@ -264,15 +275,16 @@
       double expected_score,
       const std::vector<std::string>& expected_errors =
           std::vector<std::string>(),
+      absl::optional<uint32_t> expected_data_version = absl::nullopt,
       const absl::optional<GURL>& expected_debug_loss_report_url =
           absl::nullopt,
       const absl::optional<GURL>& expected_debug_win_report_url =
           absl::nullopt) {
     base::RunLoop run_loop;
-    RunScoreAdOnWorkletAsync(seller_worklet, expected_score, expected_errors,
-                             expected_debug_loss_report_url,
-                             expected_debug_win_report_url,
-                             run_loop.QuitClosure());
+    RunScoreAdOnWorkletAsync(
+        seller_worklet, expected_score, expected_errors, expected_data_version,
+        expected_debug_loss_report_url, expected_debug_win_report_url,
+        run_loop.QuitClosure());
     run_loop.Run();
   }
 
@@ -281,15 +293,17 @@
       double expected_score,
       const std::vector<std::string>& expected_errors =
           std::vector<std::string>(),
+      absl::optional<uint32_t> expected_data_version = absl::nullopt,
       const absl::optional<GURL>& expected_debug_loss_report_url =
           absl::nullopt,
       const absl::optional<GURL>& expected_debug_win_report_url =
           absl::nullopt) {
     auto seller_worklet = CreateWorklet();
     ASSERT_TRUE(seller_worklet);
-    RunScoreAdExpectingResultOnWorklet(
-        seller_worklet.get(), expected_score, expected_errors,
-        expected_debug_loss_report_url, expected_debug_win_report_url);
+    RunScoreAdExpectingResultOnWorklet(seller_worklet.get(), expected_score,
+                                       expected_errors, expected_data_version,
+                                       expected_debug_loss_report_url,
+                                       expected_debug_win_report_url);
   }
 
   // Configures `url_loader_factory_` to return a report_result() script created
@@ -335,7 +349,8 @@
     seller_worklet->ReportResult(
         auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
-        browser_signal_desireability_,
+        browser_signal_desireability_, browser_signal_data_version_.value_or(0),
+        browser_signal_data_version_.has_value(),
         base::BindOnce(
             [](const absl::optional<std::string>& expected_signals_for_winner,
                const absl::optional<GURL>& expected_report_url,
@@ -358,7 +373,8 @@
     seller_worklet->ReportResult(
         auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
-        browser_signal_desireability_,
+        browser_signal_desireability_, browser_signal_data_version_.value_or(0),
+        browser_signal_data_version_.has_value(),
         base::BindOnce([](const absl::optional<std::string>& signals_for_winner,
                           const absl::optional<GURL>& report_url,
                           const std::vector<std::string>& errors) {
@@ -465,6 +481,7 @@
   std::vector<GURL> browser_signal_ad_components_;
   uint32_t browser_signal_bidding_duration_msecs_;
   double browser_signal_desireability_;
+  absl::optional<uint32_t> browser_signal_data_version_;
 
   // Reuseable run loop for disconnection errors.
   std::unique_ptr<base::RunLoop> disconnect_run_loop_;
@@ -547,20 +564,6 @@
       {"https://url.test/:5 Uncaught ReferenceError: Date is not defined."});
 }
 
-TEST_F(SellerWorkletTest, ScoreAdLogAndError) {
-  const char kScript[] = R"(
-    function scoreAd() {
-      console.log("Logging");
-      return "hello";
-    }
-  )";
-
-  RunScoreAdWithJavascriptExpectingResult(
-      kScript, 0,
-      {"https://url.test/ [Log]: Logging",
-       "https://url.test/ scoreAd() did not return a valid number."});
-}
-
 TEST_F(SellerWorkletTest, ScoreAdMedata) {
   ad_metadata_ = R"("foo")";
   RunScoreAdWithReturnValueExpectingResult(R"(adMetadata === "foo" ? 4 : 0)",
@@ -686,8 +689,8 @@
 
   // Successful download case.
 
-  AddJsonResponse(&url_loader_factory_, kNoComponentSignalsUrl,
-                  kTrustedScoringSignalsResponse);
+  AddVersionedJsonResponse(&url_loader_factory_, kNoComponentSignalsUrl,
+                           kTrustedScoringSignalsResponse, /*data_version=*/1);
 
   // Each call should cause the clock to advance exactly `kAutoSendDelay`
   // milliseconds before the request is send over the wire, waiting for other
@@ -695,10 +698,12 @@
   RunScoreAdWithReturnValueExpectingResultInExactTime(
       "trustedScoringSignals.renderUrl['https://render.url.test/']",
       4 /* Magic value in trustedScoringSignals */,
-      TrustedSignalsRequestManager::kAutoSendDelay);
+      TrustedSignalsRequestManager::kAutoSendDelay, /*expected_errors=*/{},
+      /*expected_data_version=*/1);
   RunScoreAdWithReturnValueExpectingResultInExactTime(
       "trustedScoringSignals.adComponentRenderUrls === undefined ? 1 : 0", 1,
-      TrustedSignalsRequestManager::kAutoSendDelay);
+      TrustedSignalsRequestManager::kAutoSendDelay, /*expected_errors=*/{},
+      /*expected_data_version=*/1);
 
   // A network error when fetching the scoring signals results in null
   // `trustedScoringSignals`. This case is just before the component ad test
@@ -716,26 +721,29 @@
 
   browser_signal_ad_components_ = {GURL("https://component1.test/"),
                                    GURL("https://component2.test/")};
-  AddJsonResponse(
+  AddVersionedJsonResponse(
       &url_loader_factory_,
       GURL("https://url.test/trusted_scoring_signals?hostname=window.test"
            "&renderUrls=https%3A%2F%2Frender.url.test%2F"
            "&adComponentRenderUrls=https%3A%2F%2Fcomponent1.test%2F,"
            "https%3A%2F%2Fcomponent2.test%2F"),
-      kTrustedScoringSignalsResponse);
+      kTrustedScoringSignalsResponse, /*data_version=*/5);
 
   RunScoreAdWithReturnValueExpectingResultInExactTime(
       "trustedScoringSignals.renderUrl['https://render.url.test/']",
       4 /* Magic value in trustedScoringSignals */,
-      TrustedSignalsRequestManager::kAutoSendDelay);
+      TrustedSignalsRequestManager::kAutoSendDelay, /*expected_errors=*/{},
+      /*expected_data_version=*/5);
   RunScoreAdWithReturnValueExpectingResultInExactTime(
       "trustedScoringSignals.adComponentRenderUrls['https://component1.test/']",
       1 /* Magic value in trustedScoringSignals */,
-      TrustedSignalsRequestManager::kAutoSendDelay);
+      TrustedSignalsRequestManager::kAutoSendDelay, /*expected_errors=*/{},
+      /*expected_data_version=*/5);
   RunScoreAdWithReturnValueExpectingResultInExactTime(
       "trustedScoringSignals.adComponentRenderUrls['https://component2.test/']",
       2 /* Magic value in trustedScoringSignals */,
-      TrustedSignalsRequestManager::kAutoSendDelay);
+      TrustedSignalsRequestManager::kAutoSendDelay, /*expected_errors=*/{},
+      /*expected_data_version=*/5);
 }
 
 // Test the case of a bunch of ScoreAd() calls in parallel, all started before
@@ -750,6 +758,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/absl::nullopt,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -789,6 +798,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/absl::nullopt,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -850,6 +860,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/2 * i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/i,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -872,8 +883,8 @@
         top_window_origin_.host().c_str(), i));
     std::string response_body = base::StringPrintf(
         R"({"renderUrls": {"https://foo/%zu": %zu}})", i, 2 * i);
-    AddJsonResponse(&url_loader_factory_, trusted_scoring_signals,
-                    response_body);
+    AddVersionedJsonResponse(&url_loader_factory_, trusted_scoring_signals,
+                             response_body, /*data_version=*/i);
   }
   run_loop.Run();
 
@@ -914,6 +925,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/2 * i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/absl::nullopt,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -969,6 +981,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/2 * i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/10,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -1006,7 +1019,8 @@
   }
   response_body =
       base::StringPrintf(R"({"renderUrls": {%s}})", response_body.c_str());
-  AddJsonResponse(&url_loader_factory_, GURL(request_url), response_body);
+  AddVersionedJsonResponse(&url_loader_factory_, GURL(request_url),
+                           response_body, /*data_version=*/10);
 
   // All ScoreAd() calls should succeed with the expected scores.
   run_loop.Run();
@@ -1032,6 +1046,7 @@
     browser_signal_render_url_ = GURL(base::StringPrintf("https://foo/%zu", i));
     RunScoreAdOnWorkletAsync(seller_worklet.get(), /*expected_score=*/2 * i,
                              /*expected_errors=*/std::vector<std::string>(),
+                             /*expected_data_version=*/10,
                              /*expected_debug_loss_report_url=*/absl::nullopt,
                              /*expected_debug_win_report_url=*/absl::nullopt,
                              base::BindLambdaForTesting([&]() {
@@ -1061,7 +1076,8 @@
   }
   response_body =
       base::StringPrintf(R"({"renderUrls": {%s}})", response_body.c_str());
-  AddJsonResponse(&url_loader_factory_, GURL(request_url), response_body);
+  AddVersionedJsonResponse(&url_loader_factory_, GURL(request_url),
+                           response_body, /*data_version=*/10);
 
   // Spin run loop so the response is handled. No ScoreAdCalls should complete
   // yet.
@@ -1373,11 +1389,13 @@
           browser_signal_interest_group_owner_, browser_signal_render_url_,
           browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
           base::BindLambdaForTesting(
-              [&run_loop](double score,
+              [&run_loop](double score, uint32_t data_version,
+                          bool has_data_version,
                           const absl::optional<GURL>& debug_loss_report_url,
                           const absl::optional<GURL>& debug_win_report_url,
                           const std::vector<std::string>& errors) {
                 EXPECT_EQ(2, score);
+                EXPECT_FALSE(has_data_version);
                 EXPECT_TRUE(errors.empty());
                 run_loop.Quit();
               }));
@@ -1390,6 +1408,8 @@
           auction_ad_config_non_shared_params_.Clone(),
           browser_signal_interest_group_owner_, browser_signal_render_url_,
           bid_, browser_signal_desireability_,
+          browser_signal_data_version_.value_or(0),
+          browser_signal_data_version_.has_value(),
           base::BindLambdaForTesting(
               [&run_loop](const absl::optional<std::string>& signals_for_winner,
                           const absl::optional<GURL>& report_url,
@@ -1414,7 +1434,8 @@
       ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
       browser_signal_interest_group_owner_, browser_signal_render_url_,
       browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
-      base::BindOnce([](double score,
+      base::BindOnce([](double score, uint32_t data_version,
+                        bool has_data_version,
                         const absl::optional<GURL>& debug_loss_report_url,
                         const absl::optional<GURL>& debug_win_report_url,
                         const std::vector<std::string>& errors) {
@@ -1438,7 +1459,8 @@
   seller_worklet->ReportResult(
       auction_ad_config_non_shared_params_.Clone(),
       browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
-      browser_signal_desireability_,
+      browser_signal_desireability_, browser_signal_data_version_.value_or(0),
+      browser_signal_data_version_.has_value(),
       base::BindOnce([](const absl::optional<std::string>& signals_for_winner,
                         const absl::optional<GURL>& report_url,
                         const std::vector<std::string>& errors) {
@@ -1464,8 +1486,9 @@
   // is paused.
   base::RunLoop run_loop;
   RunScoreAdOnWorkletAsync(
-      worklet.get(), /*expected_score=*/10,
-      /*expected_errors=*/{}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      worklet.get(), /*expected_score=*/10, /*expected_errors=*/{},
+      /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop.QuitClosure());
 
   // Give it a chance to fetch.
@@ -1543,7 +1566,8 @@
   base::RunLoop run_loop1;
   RunScoreAdOnWorkletAsync(
       worklet1.get(), /*expected_score=*/1,
-      /*expected_errors=*/{}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop1.QuitClosure());
 
   decision_logic_url_ = kUrl2;
@@ -1553,7 +1577,8 @@
   base::RunLoop run_loop2;
   RunScoreAdOnWorkletAsync(
       worklet2.get(), /*expected_score=*/2,
-      /*expected_errors=*/{}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop2.QuitClosure());
 
   int id1 = worklet_impl1->context_group_id_for_testing();
@@ -1665,10 +1690,10 @@
   decision_logic_url_ = GURL(kUrl1);
   auto worklet1 = CreateWorklet(true /* pause_for_debugger_on_start */);
   base::RunLoop run_loop1;
-  RunScoreAdOnWorkletAsync(worklet1.get(), 100.5, {},
-                           /*expected_debug_loss_report_url=*/absl::nullopt,
-                           /*expected_debug_win_report_url=*/absl::nullopt,
-                           run_loop1.QuitClosure());
+  RunScoreAdOnWorkletAsync(
+      worklet1.get(), 100.5, {}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
+      /*expected_debug_win_report_url=*/absl::nullopt, run_loop1.QuitClosure());
 
   decision_logic_url_ = GURL(kUrl2);
   auto worklet2 = CreateWorklet(true /* pause_for_debugger_on_start */);
@@ -1676,12 +1701,13 @@
   RunScoreAdOnWorkletAsync(
       worklet2.get(), 0,
       {"http://example.org/second.js scoreAd() did not return a valid number."},
+      /*expected_data_version=*/absl::nullopt,
       /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop2.QuitClosure());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent1, agent2;
-  worklet1->ConnectDevToolsAgent(agent1.BindNewPipeAndPassReceiver());
-  worklet2->ConnectDevToolsAgent(agent2.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent1, agent2;
+  worklet1->ConnectDevToolsAgent(agent1.BindNewEndpointAndPassReceiver());
+  worklet2->ConnectDevToolsAgent(agent2.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug1(std::move(agent1), "123",
                                  true /* use_binary_protocol */);
@@ -1813,11 +1839,12 @@
   auto worklet = CreateWorklet(true /* pause_for_debugger_on_start */);
   base::RunLoop run_loop;
   RunScoreAdOnWorkletAsync(
-      worklet.get(), 1.0, {}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      worklet.get(), 1.0, {}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop.QuitClosure());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent;
-  worklet->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
+  worklet->ConnectDevToolsAgent(agent.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug(std::move(agent), "123",
                                 true /* use_binary_protocol */);
@@ -1884,7 +1911,8 @@
   // remove it.
   base::RunLoop run_loop3;
   RunScoreAdOnWorkletAsync(
-      worklet.get(), 1.0, {}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      worklet.get(), 1.0, {}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, run_loop3.QuitClosure());
 
   TestDevToolsAgentClient::Event breakpoint_hit3 =
@@ -1916,8 +1944,8 @@
   auto worklet = CreateWorklet(/*pause_for_debugger_on_start=*/true);
   RunScoreAdOnWorkletExpectingCallbackNeverInvoked(worklet.get());
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent;
-  worklet->ConnectDevToolsAgent(agent.BindNewPipeAndPassReceiver());
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
+  worklet->ConnectDevToolsAgent(agent.BindNewEndpointAndPassReceiver());
 
   TestDevToolsAgentClient debug(std::move(agent), "123",
                                 /*use_binary_protocol=*/true);
@@ -1942,7 +1970,8 @@
       R"({"id":4,"method":"Runtime.runIfWaitingForDebugger","params":{}})");
 
   RunScoreAdOnWorkletAsync(
-      worklet.get(), 1.0, {}, /*expected_debug_loss_report_url=*/absl::nullopt,
+      worklet.get(), 1.0, {}, /*expected_data_version=*/absl::nullopt,
+      /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt, base::BindOnce([]() {
         ADD_FAILURE() << "scoreAd shouldn't actually get to finish.";
       }));
@@ -1993,8 +2022,8 @@
           "1",
           R"(forDebuggingOnly.reportAdAuctionLoss("https://loss.url");
             forDebuggingOnly.reportAdAuctionWin("https://win.url"))"),
-      1, /*expected_errors=*/{}, GURL("https://loss.url"),
-      GURL("https://win.url"));
+      1, /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
+      GURL("https://loss.url"), GURL("https://win.url"));
 
   // Should keep debug report URLs when score <= 0.
   RunScoreAdWithJavascriptExpectingResult(
@@ -2002,18 +2031,19 @@
           "-1",
           R"(forDebuggingOnly.reportAdAuctionLoss("https://loss.url");
             forDebuggingOnly.reportAdAuctionWin("https://win.url"))"),
-      0, /*expected_errors=*/{}, GURL("https://loss.url"),
-      GURL("https://win.url"));
+      0, /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
+      GURL("https://loss.url"), GURL("https://win.url"));
 
   // It's OK to call one API but not the other.
   RunScoreAdWithJavascriptExpectingResult(
       CreateScoreAdScript(
           "1", R"(forDebuggingOnly.reportAdAuctionLoss("https://loss.url"))"),
-      1, /*expected_errors=*/{}, GURL("https://loss.url"));
+      1, /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
+      GURL("https://loss.url"));
   RunScoreAdWithJavascriptExpectingResult(
       CreateScoreAdScript(
           "1", R"(forDebuggingOnly.reportAdAuctionWin("https://win.url"))"),
-      1, /*expected_errors=*/{},
+      1, /*expected_errors=*/{}, /*expected_data_version=*/absl::nullopt,
       /*expected_debug_loss_report_url=*/absl::nullopt,
       GURL("https://win.url"));
 
@@ -2025,6 +2055,7 @@
           R"(forDebuggingOnly.reportAdAuctionLoss("https://loss.url");
             forDebuggingOnly.reportAdAuctionWin("https://win.url"))"),
       0, {"https://url.test/ scoreAd() did not return a valid number."},
+      /*expected_data_version=*/absl::nullopt,
       /*expected_debug_loss_report_url=*/absl::nullopt,
       /*expected_debug_win_report_url=*/absl::nullopt);
 }
@@ -2149,7 +2180,8 @@
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
         base::BindLambdaForTesting(
-            [&run_loop](double score,
+            [&run_loop](double score, uint32_t data_version,
+                        bool has_data_version,
                         const absl::optional<GURL>& debug_loss_report_url,
                         const absl::optional<GURL>& debug_win_report_url,
                         const std::vector<std::string>& errors) {
diff --git a/content/services/auction_worklet/trusted_signals.cc b/content/services/auction_worklet/trusted_signals.cc
index 7bf27f0..1cbd4553 100644
--- a/content/services/auction_worklet/trusted_signals.cc
+++ b/content/services/auction_worklet/trusted_signals.cc
@@ -19,6 +19,7 @@
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "gin/converter.h"
 #include "net/base/escape.h"
+#include "net/base/parse_number.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "url/gurl.h"
 #include "v8/include/v8-context.h"
@@ -141,14 +142,18 @@
 }  // namespace
 
 TrustedSignals::Result::Result(
-    std::map<std::string, std::string> bidder_json_data)
-    : bidder_json_data_(std::move(bidder_json_data)) {}
+    std::map<std::string, std::string> bidder_json_data,
+    absl::optional<uint32_t> data_version)
+    : bidder_json_data_(std::move(bidder_json_data)),
+      data_version_(data_version) {}
 
 TrustedSignals::Result::Result(
     std::map<std::string, std::string> render_url_json_data,
-    std::map<std::string, std::string> ad_component_json_data)
+    std::map<std::string, std::string> ad_component_json_data,
+    absl::optional<uint32_t> data_version)
     : render_url_json_data_(std::move(render_url_json_data)),
-      ad_component_json_data_(std::move(ad_component_json_data)) {}
+      ad_component_json_data_(std::move(ad_component_json_data)),
+      data_version_(data_version) {}
 
 v8::Local<v8::Object> TrustedSignals::Result::GetBiddingSignals(
     AuctionV8Helper* v8_helper,
@@ -281,8 +286,10 @@
                      base::Unretained(this)));
 }
 
-void TrustedSignals::OnDownloadComplete(std::unique_ptr<std::string> body,
-                                        absl::optional<std::string> error_msg) {
+void TrustedSignals::OnDownloadComplete(
+    std::unique_ptr<std::string> body,
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    absl::optional<std::string> error_msg) {
   // The downloader's job is done, so clean it up.
   auction_downloader_.reset();
 
@@ -294,7 +301,7 @@
                      v8_helper_, trusted_signals_url_,
                      std::move(bidding_signals_keys_), std::move(render_urls_),
                      std::move(ad_component_render_urls_), std::move(body),
-                     std::move(error_msg),
+                     std::move(headers), std::move(error_msg),
                      base::SequencedTaskRunnerHandle::Get(),
                      weak_ptr_factory.GetWeakPtr()));
 }
@@ -307,6 +314,7 @@
     absl::optional<std::set<std::string>> render_urls,
     absl::optional<std::set<std::string>> ad_component_render_urls,
     std::unique_ptr<std::string> body,
+    scoped_refptr<net::HttpResponseHeaders> headers,
     absl::optional<std::string> error_msg,
     scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
     base::WeakPtr<TrustedSignals> weak_instance) {
@@ -315,9 +323,21 @@
                              nullptr, std::move(error_msg));
     return;
   }
-
   DCHECK(!error_msg.has_value());
 
+  uint32_t data_version;
+  std::string data_version_string;
+  if (headers &&
+      headers->GetNormalizedHeader("Data-Version", &data_version_string) &&
+      !net::ParseUint32(data_version_string, &data_version)) {
+    std::string error = base::StringPrintf(
+        "Rejecting load of %s due to invalid Data-Version header: %s",
+        signals_url.spec().c_str(), data_version_string.c_str());
+    PostCallbackToUserThread(std::move(user_thread_task_runner), weak_instance,
+                             nullptr, std::move(error));
+    return;
+  }
+
   AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper.get());
   v8::Context::Scope context_scope(v8_helper->scratch_context());
 
@@ -336,10 +356,15 @@
 
   scoped_refptr<Result> result;
 
+  absl::optional<uint32_t> maybe_data_version;
+  if (!data_version_string.empty())
+    maybe_data_version = data_version;
+
   if (bidding_signals_keys) {
     // Handle bidding signals case.
     result = base::MakeRefCounted<Result>(
-        ParseKeyValueMap(v8_helper.get(), v8_object, *bidding_signals_keys));
+        ParseKeyValueMap(v8_helper.get(), v8_object, *bidding_signals_keys),
+        maybe_data_version);
   } else {
     // Handle scoring signals case.
     result = base::MakeRefCounted<Result>(
@@ -347,7 +372,8 @@
                               *render_urls),
         ParseChildKeyValueMap(v8_helper.get(), v8_object,
                               "adComponentRenderUrls",
-                              *ad_component_render_urls));
+                              *ad_component_render_urls),
+        maybe_data_version);
   }
 
   PostCallbackToUserThread(std::move(user_thread_task_runner), weak_instance,
diff --git a/content/services/auction_worklet/trusted_signals.h b/content/services/auction_worklet/trusted_signals.h
index 10357a3..12a5cf4 100644
--- a/content/services/auction_worklet/trusted_signals.h
+++ b/content/services/auction_worklet/trusted_signals.h
@@ -14,6 +14,7 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "net/http/http_response_headers.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -47,11 +48,13 @@
   class Result : public base::RefCountedThreadSafe<Result> {
    public:
     // Constructor for bidding signals.
-    explicit Result(std::map<std::string, std::string> bidder_json_data);
+    Result(std::map<std::string, std::string> bidder_json_data,
+           absl::optional<uint32_t> data_version);
 
     // Constructor for scoring signals.
     Result(std::map<std::string, std::string> render_url_json_data,
-           std::map<std::string, std::string> ad_component_json_data);
+           std::map<std::string, std::string> ad_component_json_data,
+           absl::optional<uint32_t> data_version);
 
     explicit Result(const Result&) = delete;
     Result& operator=(const Result&) = delete;
@@ -81,6 +84,8 @@
         const GURL& render_url,
         const std::vector<std::string>& ad_component_render_urls) const;
 
+    absl::optional<uint32_t> GetDataVersion() const { return data_version_; }
+
    private:
     friend class base::RefCountedThreadSafe<Result>;
 
@@ -94,6 +99,9 @@
         render_url_json_data_;
     const absl::optional<std::map<std::string, std::string>>
         ad_component_json_data_;
+
+    // Data version associated with the trusted signals.
+    const absl::optional<uint32_t> data_version_;
   };
 
   using LoadSignalsCallback =
@@ -146,6 +154,7 @@
                      const GURL& full_signals_url);
 
   void OnDownloadComplete(std::unique_ptr<std::string> body,
+                          scoped_refptr<net::HttpResponseHeaders> headers,
                           absl::optional<std::string> error_msg);
 
   // Parses the response body on the V8 thread, and extracts values associated
@@ -157,6 +166,7 @@
       absl::optional<std::set<std::string>> render_urls,
       absl::optional<std::set<std::string>> ad_component_render_urls,
       std::unique_ptr<std::string> body,
+      scoped_refptr<net::HttpResponseHeaders> headers,
       absl::optional<std::string> error_msg,
       scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
       base::WeakPtr<TrustedSignals> weak_instance);
diff --git a/content/services/auction_worklet/trusted_signals_unittest.cc b/content/services/auction_worklet/trusted_signals_unittest.cc
index aad7fc7f..88a7bcd 100644
--- a/content/services/auction_worklet/trusted_signals_unittest.cc
+++ b/content/services/auction_worklet/trusted_signals_unittest.cc
@@ -582,5 +582,39 @@
   event_handle->Signal();
 }
 
+TEST_F(TrustedSignalsTest, ScoringSignalsWithDataVersion) {
+  AddVersionedJsonResponse(
+      &url_loader_factory_,
+      GURL("https://url.test/"
+           "?hostname=publisher&renderUrls=https%3A%2F%2Ffoo.test%2F"),
+      kBaseScoringJson, 2u);
+  scoped_refptr<TrustedSignals::Result> signals =
+      FetchScoringSignals(/*render_urls=*/{"https://foo.test/"},
+                          /*ad_component_render_urls=*/{}, kHostname);
+  ASSERT_TRUE(signals);
+  EXPECT_EQ(R"({"renderUrl":{"https://foo.test/":1}})",
+            ExtractScoringSignals(signals.get(),
+                                  /*render_url=*/GURL("https://foo.test/"),
+                                  /*ad_component_render_urls=*/{}));
+  EXPECT_FALSE(error_msg_.has_value());
+  EXPECT_EQ(2u, signals->GetDataVersion());
+}
+
+TEST_F(TrustedSignalsTest, ScoringSignalsWithInvalidDataVersion) {
+  AddResponse(&url_loader_factory_,
+              GURL("https://url.test/"
+                   "?hostname=publisher&renderUrls=https%3A%2F%2Ffoo.test%2F"),
+              kJsonMimeType, absl::nullopt, kBaseScoringJson,
+              "X-Allow-FLEDGE: true\nData-Version: 2.0");
+  scoped_refptr<TrustedSignals::Result> signals =
+      FetchScoringSignals(/*render_urls=*/{"https://foo.test/"},
+                          /*ad_component_render_urls=*/{}, kHostname);
+  ASSERT_TRUE(error_msg_.has_value());
+  EXPECT_EQ(
+      "Rejecting load of https://url.test/ due to invalid Data-Version header: "
+      "2.0",
+      error_msg_.value());
+}
+
 }  // namespace
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/worklet_devtools_debug_test_util.cc b/content/services/auction_worklet/worklet_devtools_debug_test_util.cc
index 80c3a97..f90274af 100644
--- a/content/services/auction_worklet/worklet_devtools_debug_test_util.cc
+++ b/content/services/auction_worklet/worklet_devtools_debug_test_util.cc
@@ -40,7 +40,7 @@
 }  // namespace
 
 TestDevToolsAgentClient::TestDevToolsAgentClient(
-    mojo::Remote<blink::mojom::DevToolsAgent> agent,
+    mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent,
     std::string session_id,
     bool use_binary_protocol)
     : session_id_(std::move(session_id)),
diff --git a/content/services/auction_worklet/worklet_devtools_debug_test_util.h b/content/services/auction_worklet/worklet_devtools_debug_test_util.h
index aafd1b1..35745d7 100644
--- a/content/services/auction_worklet/worklet_devtools_debug_test_util.h
+++ b/content/services/auction_worklet/worklet_devtools_debug_test_util.h
@@ -42,9 +42,10 @@
 
   // `use_binary_protocol` determines which protocol to ask of the agent and to
   // talk to it; all methods here will always talk JSON.
-  TestDevToolsAgentClient(mojo::Remote<blink::mojom::DevToolsAgent> agent,
-                          std::string session_id,
-                          bool use_binary_protocol);
+  TestDevToolsAgentClient(
+      mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent,
+      std::string session_id,
+      bool use_binary_protocol);
   TestDevToolsAgentClient(const TestDevToolsAgentClient&) = delete;
   TestDevToolsAgentClient& operator=(const TestDevToolsAgentClient&) = delete;
   ~TestDevToolsAgentClient() override;
@@ -92,7 +93,7 @@
   std::string session_id_;
   bool use_binary_protocol_;
 
-  mojo::Remote<blink::mojom::DevToolsAgent> agent_;
+  mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent_;
   mojo::AssociatedRemote<blink::mojom::DevToolsSession> session_;
   mojo::Remote<blink::mojom::DevToolsSession> io_session_;
   mojo::AssociatedReceiver<blink::mojom::DevToolsSessionHost> receiver_;
diff --git a/content/services/auction_worklet/worklet_loader.cc b/content/services/auction_worklet/worklet_loader.cc
index 511a0c1..968c81d 100644
--- a/content/services/auction_worklet/worklet_loader.cc
+++ b/content/services/auction_worklet/worklet_loader.cc
@@ -95,6 +95,7 @@
 
 void WorkletLoaderBase::OnDownloadComplete(
     std::unique_ptr<std::string> body,
+    scoped_refptr<net::HttpResponseHeaders> headers,
     absl::optional<std::string> error_msg) {
   DCHECK(load_worklet_callback_);
 
diff --git a/content/services/auction_worklet/worklet_loader.h b/content/services/auction_worklet/worklet_loader.h
index 560640a..7bea36e 100644
--- a/content/services/auction_worklet/worklet_loader.h
+++ b/content/services/auction_worklet/worklet_loader.h
@@ -100,6 +100,7 @@
 
  private:
   void OnDownloadComplete(std::unique_ptr<std::string> body,
+                          scoped_refptr<net::HttpResponseHeaders> headers,
                           absl::optional<std::string> error_msg);
 
   static void HandleDownloadResultOnV8Thread(
diff --git a/content/services/auction_worklet/worklet_test_util.cc b/content/services/auction_worklet/worklet_test_util.cc
index 795a521..5f2834c 100644
--- a/content/services/auction_worklet/worklet_test_util.cc
+++ b/content/services/auction_worklet/worklet_test_util.cc
@@ -80,6 +80,16 @@
   AddResponse(url_loader_factory, url, kJsonMimeType, absl::nullopt, content);
 }
 
+void AddVersionedJsonResponse(network::TestURLLoaderFactory* url_loader_factory,
+                              const GURL& url,
+                              const std::string content,
+                              uint32_t data_version) {
+  std::string headers = base::StringPrintf("%s\nData-Version: %u",
+                                           kAllowFledgeHeader, data_version);
+  AddResponse(url_loader_factory, url, kJsonMimeType, absl::nullopt, content,
+              headers);
+}
+
 base::WaitableEvent* WedgeV8Thread(AuctionV8Helper* v8_helper) {
   auto event = std::make_unique<base::WaitableEvent>();
   base::WaitableEvent* event_handle = event.get();
diff --git a/content/services/auction_worklet/worklet_test_util.h b/content/services/auction_worklet/worklet_test_util.h
index 8527a4ca..54954097 100644
--- a/content/services/auction_worklet/worklet_test_util.h
+++ b/content/services/auction_worklet/worklet_test_util.h
@@ -53,6 +53,10 @@
 void AddJsonResponse(network::TestURLLoaderFactory* url_loader_factory,
                      const GURL& url,
                      const std::string content);
+void AddVersionedJsonResponse(network::TestURLLoaderFactory* url_loader_factory,
+                              const GURL& url,
+                              const std::string content,
+                              uint32_t data_version);
 
 // Adds a task to `v8_helper->v8_runner()` that blocks until the return value
 // is signaled. The returned event will be deleted afterwards.
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index de7146e..0728121 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -416,6 +416,7 @@
     "$root_gen_dir/content/browser/webrtc/resources/webrtc_internals_resources.pak",
     "$root_gen_dir/content/content_resources.pak",
     "$root_gen_dir/content/dev_ui_content_resources.pak",
+    "$root_gen_dir/content/quota_internals_resources.pak",
     "$root_gen_dir/content/shell/shell_resources.pak",
     "$root_gen_dir/content/test/web_ui_mojo_test_resources.pak",
     "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
@@ -438,6 +439,7 @@
     "//content:content_resources",
     "//content:dev_ui_content_resources",
     "//content/app/resources",
+    "//content/browser/resources:resources",
     "//content/browser/resources/media:resources",
     "//content/browser/webrtc/resources",
     "//content/test:web_ui_mojo_test_resources",
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 9a1aa0b..9391792 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -168,8 +168,7 @@
 
 void NetworkHandlerFailureCallback(
     NetworkingPrivateDelegate::FailureCallback callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   std::move(callback).Run(error_name);
 }
 
diff --git a/extensions/browser/api/vpn_provider/vpn_service.cc b/extensions/browser/api/vpn_provider/vpn_service.cc
index 3b994e1..c430b2cd 100644
--- a/extensions/browser/api/vpn_provider/vpn_service.cc
+++ b/extensions/browser/api/vpn_provider/vpn_service.cc
@@ -561,8 +561,7 @@
 void VpnService::OnCreateConfigurationFailure(
     VpnService::FailureCallback callback,
     VpnConfiguration* configuration,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   DestroyConfigurationInternal(configuration);
   std::move(callback).Run(error_name, std::string());
 }
@@ -574,8 +573,7 @@
 
 void VpnService::OnRemoveConfigurationFailure(
     VpnService::FailureCallback callback,
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   std::move(callback).Run(error_name, std::string());
 }
 
diff --git a/extensions/browser/api/vpn_provider/vpn_service.h b/extensions/browser/api/vpn_provider/vpn_service.h
index 2fee5d2..c831a5af 100644
--- a/extensions/browser/api/vpn_provider/vpn_service.h
+++ b/extensions/browser/api/vpn_provider/vpn_service.h
@@ -171,20 +171,16 @@
                                     const std::string& guid);
 
   // Callback used to indicate that configuration creation failed.
-  void OnCreateConfigurationFailure(
-      FailureCallback callback,
-      VpnConfiguration* configuration,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void OnCreateConfigurationFailure(FailureCallback callback,
+                                    VpnConfiguration* configuration,
+                                    const std::string& error_name);
 
   // Callback used to indicate that removing a configuration succeeded.
   void OnRemoveConfigurationSuccess(SuccessCallback callback);
 
   // Callback used to indicate that removing a configuration failed.
-  void OnRemoveConfigurationFailure(
-      FailureCallback callback,
-      const std::string& error_name,
-      std::unique_ptr<base::DictionaryValue> error_data);
+  void OnRemoveConfigurationFailure(FailureCallback callback,
+                                    const std::string& error_name);
 
   void OnGetShillProperties(const std::string& service_path,
                             absl::optional<base::Value> dictionary);
diff --git a/extensions/shell/browser/shell_network_controller_chromeos.cc b/extensions/shell/browser/shell_network_controller_chromeos.cc
index 2bf4d47..06335a0 100644
--- a/extensions/shell/browser/shell_network_controller_chromeos.cc
+++ b/extensions/shell/browser/shell_network_controller_chromeos.cc
@@ -29,8 +29,7 @@
 // or when connected to a non-preferred network.
 const int kScanIntervalSec = 10;
 
-void HandleEnableWifiError(const std::string& error_name,
-                           std::unique_ptr<base::DictionaryValue> error_data) {
+void HandleEnableWifiError(const std::string& error_name) {
   LOG(WARNING) << "Unable to enable wifi: " << error_name;
 }
 
@@ -220,8 +219,7 @@
 }
 
 void ShellNetworkController::HandleConnectionError(
-    const std::string& error_name,
-    std::unique_ptr<base::DictionaryValue> error_data) {
+    const std::string& error_name) {
   LOG(WARNING) << "Unable to connect to network: " << error_name;
   state_ = STATE_IDLE;
 }
diff --git a/extensions/shell/browser/shell_network_controller_chromeos.h b/extensions/shell/browser/shell_network_controller_chromeos.h
index e7e8b28..163c3ef 100644
--- a/extensions/shell/browser/shell_network_controller_chromeos.h
+++ b/extensions/shell/browser/shell_network_controller_chromeos.h
@@ -11,7 +11,6 @@
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-#include "base/values.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace extensions {
@@ -65,8 +64,7 @@
 
   // Handles a successful or failed connection attempt.
   void HandleConnectionSuccess();
-  void HandleConnectionError(const std::string& error_name,
-                             std::unique_ptr<base::DictionaryValue> error_data);
+  void HandleConnectionError(const std::string& error_name);
 
   // Current status of communication with the chromeos::NetworkStateHandler.
   // This is tracked to avoid sending duplicate requests before the handler has
diff --git a/gpu/command_buffer/service/shared_image_backing_raw_draw.cc b/gpu/command_buffer/service/shared_image_backing_raw_draw.cc
index 48b9300e..37d9ea5 100644
--- a/gpu/command_buffer/service/shared_image_backing_raw_draw.cc
+++ b/gpu/command_buffer/service/shared_image_backing_raw_draw.cc
@@ -230,11 +230,7 @@
     paint_op_buffer_->Playback(surface->getCanvas(), playback_params);
   }
 
-  // Insert resolveMSAA in surface's command stream, so if the surface,
-  // otherwise gr_context->flush() call will not resolve to the wrapped
-  // backend_texture_.
-  surface->resolveMSAA();
-
+  surface->flush();
   return true;
 }
 
diff --git a/gpu/command_buffer/service/shared_image_video_image_reader.cc b/gpu/command_buffer/service/shared_image_video_image_reader.cc
index 0f5ec11e..3cabbcf4 100644
--- a/gpu/command_buffer/service/shared_image_video_image_reader.cc
+++ b/gpu/command_buffer/service/shared_image_video_image_reader.cc
@@ -542,7 +542,14 @@
   }
 
   void EndReadAccess(gfx::GpuFenceHandle release_fence) override {
-    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
+    // Note that we dont need to hold onto DrDc lock here. If we add DrDc lock
+    // here then there could be a situation where
+    // FrameInfoHelper::GetFrameInfo() can hold DrDc lock from gpu main thread
+    // while waiting for the buffer to be available and EndReadAccess could
+    // wait on the same lock here from drdc thread, resulting in buffer not
+    // being released and hence deadlock.
+    // crbug.com/1262990 for more details.
+
     DCHECK(release_fence.is_null());
     if (gl_image_) {
       if (scoped_hardware_buffer_) {
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index 2d6a33b7e..b544f67 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -8,16 +8,15 @@
 base/files 1 1
 base/sampling_heap_profiler 1 1
 base/synchronization 3 2
-base/test 3 3
+base/test 1 1
 base/test/data/pe_image_reader 1 1
-base/trace_event 9 3
 build 10 3
 build/android 5 3
 build/android/gradle 1 1
 build/android/gyp 4 3
 build/android/gyp/util 2 1
 build/android/pylib/results/flakiness_dashboard 5 2
-build/android/pylib/utils 4 4
+build/android/pylib/utils 2 2
 build/android/unused_resources 1 1
 build/config 2 2
 build/config/fuchsia 1 1
@@ -26,15 +25,12 @@
 build/skia_gold_common 1 1
 build/toolchain/linux/unbundle 1 1
 build/toolchain/win/rc 1 1
-build/util/lib/common 3 2
+build/util/lib/common 1 1
 cc/input 2 2
 chrome/android/java/src/org/chromium/chrome/browser/contextualsearch 1 1
 chrome/android/java/src/org/chromium/chrome/browser/omaha 2 2
 chrome/android/java/src/org/chromium/chrome/browser/services/gcm 2 1
-chrome/android/java/src/org/chromium/chrome/browser/sync 10 2
-chrome/android/javatests/src/org/chromium/chrome/browser/sync 2 2
-chrome/android/junit/src/org/chromium/chrome/browser/sync 4 1
-chrome/browser 4 3
+chrome/browser 3 3
 chrome/browser/android/oom_intervention 1 1
 chrome/browser/ash/arc/bluetooth 1 1
 chrome/browser/ash/ownership 4 2
@@ -43,7 +39,6 @@
 chrome/browser/ash/scanning 1 1
 chrome/browser/ash/settings 6 2
 chrome/browser/chromeos/extensions 8 2
-chrome/browser/devtools 3 1
 chrome/browser/download 1 1
 chrome/browser/extensions 2 2
 chrome/browser/extensions/api/commands 3 1
@@ -51,14 +46,12 @@
 chrome/browser/federated_learning 1 1
 chrome/browser/feed/android 1 1
 chrome/browser/first_run 1 1
-chrome/browser/loader 4 1
 chrome/browser/mac 1 1
 chrome/browser/metrics 4 2
 chrome/browser/prefs 2 2
 chrome/browser/privacy 1 1
 chrome/browser/resources/settings/site_settings 2 2
 chrome/browser/speech/extension_api 1 1
-chrome/browser/sync/test/integration 1 1
 chrome/browser/ui 2 1
 chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox 1 1
 chrome/browser/ui/commander 1 1
@@ -80,23 +73,20 @@
 chrome/chrome_elf/crash 1 1
 chrome/chrome_elf/third_party_dlls 85 10
 chrome/common 6 3
-chrome/common/extensions 4 3
+chrome/common/extensions 1 1
 chrome/common/extensions/api 2 2
-chrome/common/extensions/permissions 4 1
 chrome/docs 2 2
 chrome/installer/gcapi_mac 5 2
 chrome/installer/setup 2 1
 chrome/installer/util 2 2
 chrome/install_static 1 1
+chromeos/network/docs 1 1
 chromeos/process_proxy 6 5
 chromeos/third_party/android_bionic_libc 1 1
-chrome/service/cloud_print 1 1
-chrome/test/base 1 1
 chrome/test/chromedriver 1 1
 chrome/test/chromedriver/chrome 1 1
 chrome/test/chromedriver/docs 1 1
 chrome/test/chromedriver/server 1 1
-chrome/test/chromedriver/test 2 1
 chrome/test/data/dom_checker 1 1
 chrome/test/data/dromaeo 13 1
 chrome/test/data/dromaeo/lib 4 2
@@ -117,11 +107,10 @@
 components/cast/named_message_port_connector 2 2
 components/cbor 2 2
 components/certificate_transparency 1 1
-components/data_reduction_proxy/core/common 1 1
 components/domain_reliability 4 2
 components/federated_learning 1 1
 components/infobars/android/java/src/org/chromium/components/infobars 1 1
-components/metrics 2 2
+components/metrics 1 1
 components/page_info 2 2
 components/password_manager/core/browser 1 1
 components/password_manager/core/browser/site_affiliation 1 1
@@ -130,7 +119,6 @@
 components/policy/proto 2 1
 components/policy/resources 166 37
 components/reporting 1 1
-components/reporting/util 1 1
 components/rlz 1 1
 components/safe_browsing/core/browser/db 2 1
 components/safe_browsing/core/common 1 1
@@ -139,8 +127,7 @@
 components/security_interstitials/content/android/java/src/org/chromium/components/security_interstitials 1 1
 components/services/storage/dom_storage 1 1
 components/strictmode/android/java/src/org/chromium/components/strictmode 4 3
-components/sync/base 4 3
-components/sync/driver 3 2
+components/sync/base 1 1
 components/sync/engine/nigori 1 1
 components/sync/protocol 2 1
 components/test/data/autofill/automated_integration 2 1
@@ -148,48 +135,39 @@
 components/test/data/autofill/automated_integration/autofill_test 3 3
 components/test/data/autofill/heuristics/input 6 3
 components/test/data/optimization_guide 1 1
-components/ukm 8 3
+components/ukm 7 3
 components/url_formatter 1 1
 components/url_formatter/spoof_checks 1 1
 components/url_formatter/spoof_checks/common_words/data 2 1
-components/viz/service/display 2 2
 components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client 1 1
-components/webapps/services/web_app_origin_association 1 1
-components/webapps/services/web_app_origin_association/public/mojom 1 1
 components/webcrypto/algorithms 2 2
 content/browser 8 3
 content/browser/android 1 1
 content/browser/bluetooth 1 1
 content/browser/device_sensors 1 1
-content/browser/direct_sockets 1 1
 content/browser/fenced_frame 1 1
-content/browser/indexed_db 3 2
+content/browser/indexed_db 2 1
 content/browser/renderer_host 2 2
 content/browser/renderer_host/input 2 1
 content/browser/renderer_host/pepper 1 1
-content/browser/resources/gpu 1 1
 content/browser/sms 2 1
 content/browser/tracing 1 1
 content/browser/web_package 3 3
-content/browser/webrtc/resources 1 1
 content/child 1 1
-content/public/browser 2 2
-content/public/common 2 2
+content/public/android/junit/src/org/chromium/content/browser/input 1 1
+content/public/common 1 1
 content/renderer 2 1
 content/renderer/pepper 1 1
-content/test/data/gpu 1 1
-content/test/gpu/gpu_tests/skia_gold 1 1
 content/web_test/renderer 1 1
 courgette/testdata 26 4
 device/gamepad 5 3
 device/vr/public/mojom 2 1
-docs 28 16
+docs 27 15
 docs/design 2 2
 docs/enterprise 1 1
 docs/gpu 15 3
 docs/ios 9 3
 docs/linux 5 3
-docs/media/gpu 3 3
 docs/memory-infra 1 1
 docs/security 4 3
 docs/security/url_display_guidelines 1 1
@@ -198,15 +176,13 @@
 docs/speed/binary_size 1 1
 docs/speed_metrics 3 1
 docs/speed/metrics_changelog 1 1
-docs/sync 1 1
-docs/testing 4 4
+docs/testing 3 3
 docs/updater 4 2
-extensions/browser 7 5
-extensions/browser/api/audio 1 1
-extensions/common 6 6
+extensions/browser 4 3
+extensions/common 1 1
 extensions/common/api 5 4
-extensions/common/manifest_handlers 2 2
-extensions/shell/common 2 1
+extensions/common/manifest_handlers 1 1
+extensions/shell/common 1 1
 fuchsia 1 1
 google_apis/gaia 1 1
 gpu/config 1 1
@@ -242,7 +218,7 @@
 net/data/fuzzer_dictionaries 1 1
 net/disk_cache/blockfile 1 1
 net/docs 3 1
-net/http 17 1
+net/http 18 1
 net/log 1 1
 net/proxy_resolution 1 1
 net/socket 1 1
@@ -273,14 +249,15 @@
 testing/libfuzzer/proto 1 1
 testing/merge_scripts/code_coverage 1 1
 testing/scripts 2 2
+testing/scripts/variations_smoke_test_data/http_server 1 1
 third_party 1 1
-third_party/abseil-cpp 21 4
+third_party/abseil-cpp 20 3
 third_party/abseil-cpp/absl/base 1 1
 third_party/abseil-cpp/absl/base/internal 1 1
 third_party/abseil-cpp/absl/random 3 1
 third_party/abseil-cpp/absl/random/internal 1 1
 third_party/abseil-cpp/absl/status 2 1
-third_party/abseil-cpp/ci 8 6
+third_party/abseil-cpp/ci 9 7
 third_party/abseil-cpp/CMake 2 1
 third_party/abseil-cpp/CMake/Googletest 2 1
 third_party/accessibility-audit 1 1
@@ -305,8 +282,7 @@
 third_party/android_sdk 1 1
 third_party/arcore-android-sdk 1 1
 third_party/axe-core 2 1
-third_party/blink/common 3 2
-third_party/blink/manual_tests/forms 1 1
+third_party/blink/common 2 2
 third_party/blink/perf_tests/bindings/resources/data 1 1
 third_party/blink/perf_tests/css/resources 4 2
 third_party/blink/perf_tests/layout 60 2
@@ -334,96 +310,68 @@
 third_party/blink/perf_tests/speedometer/resources/todomvc/labs/architecture-examples/react/bower_components/react 1 1
 third_party/blink/public/common/permissions_policy 1 1
 third_party/blink/public/common/storage_key 1 1
-third_party/blink/public/devtools_protocol 3 2
-third_party/blink/public/mojom/digital_goods 1 1
+third_party/blink/public/devtools_protocol 2 2
 third_party/blink/public/mojom/input 1 1
 third_party/blink/public/mojom/manifest 2 2
 third_party/blink/public/mojom/permissions_policy 1 1
 third_party/blink/public/mojom/service_worker 4 3
 third_party/blink/public/mojom/storage_key 1 1
 third_party/blink/public/mojom/timing 1 1
-third_party/blink/public/mojom/web_launch 1 1
-third_party/blink/public/platform/linux 1 1
 third_party/blink/public/web 1 1
-third_party/blink/renderer/bindings 6 2
+third_party/blink/renderer/bindings 5 1
 third_party/blink/renderer/bindings/core/v8 1 1
 third_party/blink/renderer/core 1 1
-third_party/blink/renderer/core/aom 5 5
-third_party/blink/renderer/core/content_capture 1 1
-third_party/blink/renderer/core/dom 2 2
-third_party/blink/renderer/core/dom/events 1 1
 third_party/blink/renderer/core/editing/ime 1 1
-third_party/blink/renderer/core/execution_context 2 1
-third_party/blink/renderer/core/exported 1 1
-third_party/blink/renderer/core/frame 7 7
-third_party/blink/renderer/core/frame/csp 1 1
+third_party/blink/renderer/core/frame 3 3
 third_party/blink/renderer/core/html 6 5
 third_party/blink/renderer/core/html/custom 1 1
-third_party/blink/renderer/core/layout 3 3
-third_party/blink/renderer/core/layout/geometry 6 6
-third_party/blink/renderer/core/layout/ng 3 3
-third_party/blink/renderer/core/layout/ng/inline 1 1
-third_party/blink/renderer/core/layout/ng/list 1 1
-third_party/blink/renderer/core/page/scrolling 1 1
-third_party/blink/renderer/core/paint 4 4
-third_party/blink/renderer/core/paint/ng 1 1
+third_party/blink/renderer/core/layout 2 2
+third_party/blink/renderer/core/paint 2 2
 third_party/blink/renderer/core/permissions_policy 1 1
 third_party/blink/renderer/core/resize_observer 1 1
 third_party/blink/renderer/core/script 1 1
-third_party/blink/renderer/core/timing 2 2
-third_party/blink/renderer/core/web_test 1 1
+third_party/blink/renderer/core/timing 1 1
 third_party/blink/renderer/modules/accessibility 4 2
 third_party/blink/renderer/modules/badging 1 1
-third_party/blink/renderer/modules/cookie_store 1 1
 third_party/blink/renderer/modules/credentialmanager 2 1
 third_party/blink/renderer/modules/csspaint 2 1
-third_party/blink/renderer/modules/delegated_ink 1 1
 third_party/blink/renderer/modules/device_orientation 1 1
-third_party/blink/renderer/modules/direct_sockets 5 4
 third_party/blink/renderer/modules/encryptedmedia 1 1
 third_party/blink/renderer/modules/font_access 1 1
 third_party/blink/renderer/modules/imagecapture 3 1
 third_party/blink/renderer/modules/installedapp 2 2
-third_party/blink/renderer/modules/launch 4 4
-third_party/blink/renderer/modules/manifest 10 1
+third_party/blink/renderer/modules/launch 1 1
+third_party/blink/renderer/modules/manifest 4 1
 third_party/blink/renderer/modules/mediarecorder 9 1
-third_party/blink/renderer/modules/nfc 1 1
-third_party/blink/renderer/modules/payments/goods 4 4
 third_party/blink/renderer/modules/peerconnection 5 5
 third_party/blink/renderer/modules/scheduler 1 1
 third_party/blink/renderer/modules/service_worker 1 1
 third_party/blink/renderer/modules/virtualkeyboard 6 3
 third_party/blink/renderer/modules/webcodecs 3 2
+third_party/blink/renderer/modules/window_controls_overlay 3 3
 third_party/blink/renderer/modules/xr 5 5
-third_party/blink/renderer/platform 18 3
-third_party/blink/renderer/platform/fonts 2 1
-third_party/blink/renderer/platform/graphics 1 1
-third_party/blink/renderer/platform/graphics/paint 1 1
+third_party/blink/renderer/platform 3 1
 third_party/blink/renderer/platform/media 2 2
 third_party/blink/renderer/platform/scheduler/public 1 1
 third_party/blink/renderer/platform/testing 3 1
 third_party/blink/renderer/platform/text 3 2
 third_party/blink/renderer/platform/text/hyphenation 1 1
-third_party/blink/renderer/platform/weborigin 1 1
-third_party/blink/renderer/platform/wtf 15 1
-third_party/blink/tools/blinkpy/common 2 1
-third_party/blink/tools/blinkpy/common/checkout 2 2
-third_party/blink/tools/blinkpy/common/config 34 1
-third_party/blink/tools/blinkpy/common/net 14 4
+third_party/blink/renderer/platform/wtf 2 2
+third_party/blink/tools/blinkpy/common 1 1
+third_party/blink/tools/blinkpy/common/checkout 1 1
+third_party/blink/tools/blinkpy/common/config 33 1
+third_party/blink/tools/blinkpy/common/net 9 3
 third_party/blink/tools/blinkpy/common/system 2 1
 third_party/blink/tools/blinkpy/style/checkers 1 1
-third_party/blink/tools/blinkpy/tool/commands 1 1
-third_party/blink/tools/blinkpy/w3c 29 10
+third_party/blink/tools/blinkpy/w3c 27 9
 third_party/blink/tools/blinkpy/web_tests 8 4
 third_party/blink/tools/blinkpy/web_tests/controllers 3 2
 third_party/blink/tools/blinkpy/web_tests/layout_package 9 2
-third_party/blink/tools/blinkpy/web_tests/port 7 4
-third_party/blink/web_tests 10 10
-third_party/blink/web_tests/accessibility 8 8
+third_party/blink/tools/blinkpy/web_tests/port 1 1
+third_party/blink/web_tests 1 1
 third_party/blink/web_tests/dom/domparsing 2 2
 third_party/blink/web_tests/dom/legacy_dom_conformance/html/level1/core 1 1
 third_party/blink/web_tests/dom/legacy_dom_conformance/xhtml/level1/core 1 1
-third_party/blink/web_tests/external 1 1
 third_party/blink/web_tests/external/wpt 2 1
 third_party/blink/web_tests/external/wpt/bluetooth 1 1
 third_party/blink/web_tests/external/wpt/bluetooth/resources 2 1
@@ -437,13 +385,11 @@
 third_party/blink/web_tests/external/wpt/css/mediaqueries 1 1
 third_party/blink/web_tests/external/wpt/docs 2 1
 third_party/blink/web_tests/external/wpt/docs/reviewing-tests 6 2
-third_party/blink/web_tests/external/wpt/docs/running-tests 1 1
-third_party/blink/web_tests/external/wpt/docs/writing-tests 22 7
+third_party/blink/web_tests/external/wpt/docs/writing-tests 21 6
 third_party/blink/web_tests/external/wpt/document-policy/font-display 13 13
 third_party/blink/web_tests/external/wpt/dom/traversal 1 1
 third_party/blink/web_tests/external/wpt/encrypted-media/content 5 5
 third_party/blink/web_tests/external/wpt/feature-policy/resources 1 1
-third_party/blink/web_tests/external/wpt/fetch/corb 2 2
 third_party/blink/web_tests/external/wpt/html/browsers/sandboxing 4 4
 third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/moving-between-documents/resources 1 1
 third_party/blink/web_tests/external/wpt/html/tools 1 1
@@ -457,14 +403,12 @@
 third_party/blink/web_tests/external/wpt/payment-request 8 8
 third_party/blink/web_tests/external/wpt/payment-request/payment-response 10 10
 third_party/blink/web_tests/external/wpt/payment-request/PaymentValidationErrors 2 2
-third_party/blink/web_tests/external/wpt/direct-sockets 3 3
 third_party/blink/web_tests/external/wpt/resources 2 2
-third_party/blink/web_tests/external/wpt/resources/chromium 3 2
+third_party/blink/web_tests/external/wpt/resources/chromium 1 1
 third_party/blink/web_tests/external/wpt/service-workers/service-worker 1 1
 third_party/blink/web_tests/external/wpt/webaudio/js 1 1
 third_party/blink/web_tests/external/wpt/webcodecs 1 1
 third_party/blink/web_tests/external/wpt/webrtc 1 1
-third_party/blink/web_tests/external/wpt/webrtc-encoded-transform 1 1
 third_party/blink/web_tests/external/wpt/webrtc/third_party/sdp 2 1
 third_party/blink/web_tests/external/wpt/websockets/stream/tentative 1 1
 third_party/blink/web_tests/external/wpt/workers 2 1
@@ -476,18 +420,17 @@
 third_party/blink/web_tests/http/tests/devtools/persistence 2 1
 third_party/blink/web_tests/http/tests/devtools/sources/debugger-pause 4 2
 third_party/blink/web_tests/http/tests/dom 2 2
-third_party/blink/web_tests/http/tests/websocket 11 11
+third_party/blink/web_tests/http/tests/websocket 10 10
 third_party/blink/web_tests/images/resources/avif 7 7
 third_party/blink/web_tests/inspector-protocol/dom-snapshot 2 1
 third_party/blink/web_tests/resize-observer 1 1
-third_party/blink/web_tests/resources 3 3
+third_party/blink/web_tests/resources 1 1
 third_party/blink/web_tests/third_party/ChromaCheck 1 1
 third_party/blink/web_tests/third_party/Tinos 1 1
 third_party/blink/web_tests/virtual/css-modules 1 1
-third_party/blink/web_tests/virtual/file-handling 1 1
-third_party/blink/web_tests/virtual/file-handling/file-handling 1 1
 third_party/blink/web_tests/virtual/json-modules 1 1
 third_party/blink/web_tests/webaudio/resources 1 1
+third_party/blink/web_tests/wpt_internal/fenced_frame/resources 1 1
 third_party/blink/web_tests/wpt_internal/html/semantics/parser-blocking-stylesheets 6 5
 third_party/boringssl 1 1
 third_party/brotli 2 1
@@ -495,14 +438,12 @@
 third_party/chaijs 5 4
 third_party/chevron 2 1
 third_party/closure_compiler/compiler 1 1
-third_party/closure_compiler/externs 31 27
+third_party/closure_compiler/externs 29 25
 third_party/crashpad/crashpad 1 1
 third_party/crashpad/crashpad/doc 10 1
 third_party/crashpad/crashpad/doc/appengine/src/crashpad-home 1 1
 third_party/crashpad/crashpad/doc/support 2 2
 third_party/crashpad/crashpad/handler 1 1
-third_party/crashpad/crashpad/infra/config 2 1
-third_party/crashpad/crashpad/infra/config/generated 1 1
 third_party/crashpad/crashpad/snapshot 1 1
 third_party/crashpad/crashpad/snapshot/fuchsia 1 1
 third_party/crashpad/crashpad/test/mac 1 1
@@ -514,6 +455,7 @@
 third_party/crashpad/crashpad/util/thread 2 1
 third_party/d3/src 399 3
 third_party/dav1d 2 2
+third_party/distributed_point_functions/code 2 1
 third_party/dom_distiller_js 1 1
 third_party/espresso 13 1
 third_party/expat 2 2
@@ -539,7 +481,7 @@
 third_party/google-closure-library/scripts/ci 9 6
 third_party/google_input_tools/src/chrome/os/sounds 1 1
 third_party/google_input_tools/third_party/closure_library/closure/goog/html 1 1
-third_party/googletest 11 2
+third_party/googletest 10 2
 third_party/google_trust_services 1 1
 third_party/grpc 2 1
 third_party/gvr-android-sdk 2 1
@@ -550,7 +492,6 @@
 third_party/instrumented_libraries/focal/scripts 1 1
 third_party/instrumented_libraries/xenial/scripts 1 1
 third_party/jsoncpp 1 1
-third_party/libaom 1 1
 third_party/libavif 1 1
 third_party/libgav1 1 1
 third_party/libpng/contrib/oss-fuzz 1 1
@@ -656,8 +597,8 @@
 third_party/protobuf/src/google/protobuf/compiler/objectivec 2 2
 third_party/protobuf/src/google/protobuf/util/internal 7 1
 third_party/pylint/pylint 4 2
-third_party/Python-Markdown 1 1
-third_party/Python-Markdown/markdown 5 2
+third_party/Python-Markdown 2 1
+third_party/Python-Markdown/markdown 4 1
 third_party/Python-Markdown/markdown/extensions 1 1
 third_party/qcms 1 1
 third_party/rust/aho_corasick/v0_7/crate/.github/workflows 2 1
@@ -721,8 +662,12 @@
 third_party/rust/regex_syntax/v0_6/crate 1 1
 third_party/rust/regex/v1/crate 1 1
 third_party/rust/regex/v1/crate/src 2 2
+third_party/rust/rstest/v0_12/crate/.github/workflows 3 2
+third_party/rust/rustc_version/v0_4/crate/.github/workflows 1 1
 third_party/rust/ryu/v1/crate 1 1
 third_party/rust/ryu/v1/crate/.github/workflows 1 1
+third_party/rust/semver/v1/crate 1 1
+third_party/rust/semver/v1/crate/.github/workflows 1 1
 third_party/rust/serde_derive/v1/crate 1 1
 third_party/rust/serde/v1/crate 1 1
 third_party/rust/serde/v1/crate/src 1 1
@@ -741,10 +686,6 @@
 third_party/six/src/documentation 1 1
 third_party/sqlite 1 1
 third_party/sqlite/scripts 1 1
-third_party/tcmalloc/chromium/src 1 1
-third_party/tcmalloc/chromium/src/gperftools 1 1
-third_party/tcmalloc/vendor/src 1 1
-third_party/tcmalloc/vendor/src/gperftools 1 1
 third_party/tensorflow-text/src 3 2
 third_party/tensorflow-text/src/docs/api_docs/python 1 1
 third_party/tensorflow-text/src/docs/api_docs/python/text 117 48
@@ -758,6 +699,7 @@
 third_party/tensorflow-text/src/tensorflow_text/python/benchmarks/test_data/uncased_L-12_H-768_A-12 3 1
 third_party/tensorflow-text/src/tensorflow_text/python/metrics 1 1
 third_party/tensorflow-text/src/tensorflow_text/python/ops/test_data 1 1
+third_party/tensorflow-text/src/third_party/icu/data 1 1
 third_party/test_fonts 15 1
 third_party/tflite_support/patches 8 2
 third_party/tflite_support/src 1 1
@@ -783,14 +725,11 @@
 third_party/tflite_support/src/tensorflow_lite_support/metadata/java/src/java/org/tensorflow/lite/support/metadata 3 1
 third_party/tflite_support/src/tensorflow_lite_support/metadata/python/tests/testdata/nl_classifier 2 1
 third_party/tflite_support/src/tensorflow_lite_support/odml/ios/image 1 1
-third_party/tlslite 1 1
-third_party/tlslite/patches 7 3
-third_party/tlslite/tlslite 3 2
 third_party/webpagereplay 1 1
 third_party/webxr_test_pages 1 1
 third_party/webxr_test_pages/webxr-samples 7 5
 third_party/woff2/src 1 1
-third_party/wpt_tools 1 1
+third_party/wpt_tools 2 2
 third_party/wpt_tools/wpt/resources 1 1
 third_party/wpt_tools/wpt/tools/lint 1 1
 third_party/wpt_tools/wpt/tools/third_party/atomicwrites 3 1
@@ -809,27 +748,26 @@
 tools/android 1 1
 tools/android/kerberos 1 1
 tools/binary_size 1 1
-tools/binary_size/libsupersize 8 3
+tools/binary_size/libsupersize 9 3
 tools/cast3p 1 1
 tools/checkperms 1 1
 tools/clang/plugins 2 2
 tools/clang/rewrite_to_chrome_style 2 1
 tools/clang/rewrite_to_chrome_style/tests 2 2
-tools/clang/scripts 1 1
 tools/cygprofile 14 3
 tools/emacs 319 3
 tools/git 2 1
 tools/infra 3 1
+tools/mac/power/protos/third_party/pprof 1 1
 tools/mb 1 1
 tools/mb/docs 1 1
 tools/md_browser 7 5
 tools/memory/partition_allocator/palloc_viewer 1 1
 tools/metrics/histograms 33 1
-tools/metrics/histograms/metadata 57 2
-tools/metrics/histograms/metadata/gpu 1 1
+tools/metrics/histograms/metadata 54 1
 tools/metrics/histograms/metadata/login 1 1
 tools/metrics/histograms/metadata/others 3 1
-tools/metrics/histograms/metadata/sb_client 3 1
+tools/metrics/histograms/metadata/sb_client 1 1
 tools/metrics/histograms/metadata/storage 5 1
 tools/metrics/ukm 1 1
 tools/perf 7 5
@@ -849,6 +787,7 @@
 tools/usb_gadget 1 1
 tools/web_bluetooth 1 1
 tools/win/RetrieveSymbols 1 1
+ui/accessibility/platform/fuchsia 1 1
 ui/base/clipboard 1 1
 ui/base/ime/ash 2 1
 ui/base/prediction 1 1
@@ -856,7 +795,7 @@
 ui/events/devices/x11 18 4
 ui/events/test 1 1
 ui/events/x 7 2
-ui/gfx 4 3
+ui/gfx 2 1
 ui/gfx/x/generated_protos 5 2
 ui/gtk 2 1
 ui/ozone/platform/drm/gpu 1 1
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 5294859..36c3de9 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b7d00f0794c8d56e1fb6cebac192ccceb982985f
\ No newline at end of file
+18a099559bd6e0f26667a34c7a8db44f2231433a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 63aac4e4..9f018f0d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a11255c12fd668c81f4346728be80d772b153f8e
\ No newline at end of file
+c24024e7316f6d2b83e616bdd63e3959ee690c6d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 70bf635d..7b48d120 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f3b8e28c11973dca7a70fb1980c74fba6148e7d4
\ No newline at end of file
+966902c49315bbc3d64babfac2d7538d4760f6b9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 5f871dd..bd034b6c 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c4331d1d4a261f4eb87f4ce350ebfbe76362fadc
\ No newline at end of file
+3b1f489588543e90b45e662080bc1c0e4799ea53
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index e75949f..6bfc279 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-44e86c7d3c51943811852503bae4b8f27add561b
\ No newline at end of file
+283d21c514036fc71d74a61565e9c429b9795ad3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 87f55508..a22128d 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-1f77dfc798e17cbcccccfb29ca099a3bb0bee895
\ No newline at end of file
+fc75038082f8b31e597a37db1ba03aaaee77f7cc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index b6737f4d..6f4f2561 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b3989bd1d33b559d344ffaaecd72275b83d69fd5
\ No newline at end of file
+6994c63f988781b60b92bfb398f5aa6385c8be34
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index adb32318..5ede1da 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-1720da5996d4de05e539e663cd5d4053ef439da8
\ No newline at end of file
+cf217df878c3e43c4b04c0dec9af5e8557909ee1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index cad98cb..1781297 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0c7ebe070e9ee91a5afdf8b83a9a8e00350200eb
\ No newline at end of file
+67ee6698be03176d59e56b3edbfa697b3a62862a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 76e1743..f540c46 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5ed467448330018795e5c8e4d8034cab69ea33fc
\ No newline at end of file
+73684d668250b3a15f1b6561ca837cab3ed81200
\ No newline at end of file
diff --git a/media/base/media_serializers.h b/media/base/media_serializers.h
index 2147aca..fde255e 100644
--- a/media/base/media_serializers.h
+++ b/media/base/media_serializers.h
@@ -454,7 +454,8 @@
     FIELD_SERIALIZE("message", status.message);
     FIELD_SERIALIZE("stack", status.frames);
     FIELD_SERIALIZE("data", status.data);
-    FIELD_SERIALIZE("causes", status.causes);
+    if (status.cause)
+      FIELD_SERIALIZE("cause", *status.cause);
     return result;
   }
 };
diff --git a/media/base/status.cc b/media/base/status.cc
index 884f541..db3524a0 100644
--- a/media/base/status.cc
+++ b/media/base/status.cc
@@ -30,8 +30,8 @@
   auto result = std::make_unique<StatusData>(group, code, message);
   for (const auto& frame : frames)
     result->frames.push_back(frame.Clone());
-  for (const auto& cause : causes)
-    result->causes.push_back(cause);
+  if (cause)
+    result->cause = cause->copy();
   result->data = data.Clone();
   return result;
 }
@@ -44,8 +44,8 @@
   message = copy.message;
   for (const auto& frame : copy.frames)
     frames.push_back(frame.Clone());
-  for (const auto& cause : copy.causes)
-    causes.push_back(cause);
+  if (copy.cause)
+    cause = copy.cause->copy();
   data = copy.data.Clone();
   return *this;
 }
diff --git a/media/base/status.h b/media/base/status.h
index 65448f8..8a674651 100644
--- a/media/base/status.h
+++ b/media/base/status.h
@@ -69,8 +69,9 @@
   // Stack frames
   std::vector<base::Value> frames;
 
-  // Causes
-  std::vector<StatusData> causes;
+  // Store a root cause. Helpful for debugging, as it can end up containing
+  // the chain of causes.
+  std::unique_ptr<StatusData> cause;
 
   // Data attached to the error
   base::Value data;
@@ -267,7 +268,9 @@
   template <typename AnyTraitsType>
   TypedStatus<T>&& AddCause(TypedStatus<AnyTraitsType>&& cause) && {
     DCHECK(data_ && cause.data_);
-    data_->causes.push_back(*cause.data_);
+    // The |cause| status is about to lose its type forever. We need to pack
+    // the group and code now to avoid losing it in the future.
+    data_->cause = std::move(cause.data_);
     return std::move(*this);
   }
 
@@ -275,7 +278,9 @@
   template <typename AnyTraitsType>
   void AddCause(TypedStatus<AnyTraitsType>&& cause) & {
     DCHECK(data_ && cause.data_);
-    data_->causes.push_back(*cause.data_);
+    // The |cause| status is about to lose its type forever. We need to pack
+    // the group and code now to avoid losing it in the future.
+    data_->cause = std::move(cause.data_);
   }
 
   inline bool operator==(Codes code) const { return code == this->code(); }
@@ -431,9 +436,6 @@
  private:
   std::unique_ptr<internal::StatusData> data_;
 
-  // Let the status sink talk about the internal data.
-  friend class StatusSink;
-
   template <typename StatusEnum, typename DataView>
   friend struct mojo::StructTraits;
 
@@ -443,10 +445,6 @@
   // Allow AddCause.
   template <typename StatusEnum>
   friend class TypedStatus;
-
-  void SetInternalData(std::unique_ptr<internal::StatusData> data) {
-    data_ = std::move(data);
-  }
 };
 
 template <typename T>
diff --git a/media/base/status_unittest.cc b/media/base/status_unittest.cc
index ba8f6b4..eeb14627 100644
--- a/media/base/status_unittest.cc
+++ b/media/base/status_unittest.cc
@@ -147,10 +147,11 @@
 TEST_F(StatusTest, SingleLayerError) {
   NormalStatus failed = FailEasily();
   base::Value actual = MediaSerialize(failed);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul);
 
   const auto& stack = actual.FindListPath("stack")->GetListDeprecated();
@@ -168,10 +169,10 @@
 TEST_F(StatusTest, MultipleErrorLayer) {
   NormalStatus failed = FailRecursively(3);
   base::Value actual = MediaSerialize(failed);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 4ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul);
 
   const auto& stack = actual.FindListPath("stack")->GetListDeprecated();
@@ -181,10 +182,10 @@
 TEST_F(StatusTest, CanHaveData) {
   NormalStatus failed = FailWithData("example", "data");
   base::Value actual = MediaSerialize(failed);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul);
 
   const auto& stack = actual.FindListPath("stack")->GetListDeprecated();
@@ -197,10 +198,10 @@
   NormalStatus failed =
       FailWithData("example", UselessThingToBeSerialized("F"));
   base::Value actual = MediaSerialize(failed);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul);
 
   const auto& stack = actual.FindListPath("stack")->GetListDeprecated();
@@ -215,15 +216,16 @@
   ASSERT_EQ(actual.DictSize(), 6ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 1ul);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul);
+  ASSERT_NE(actual.FindKey("cause"), nullptr);
 
-  base::Value& nested = actual.FindListPath("causes")->GetListDeprecated()[0];
-  ASSERT_EQ(nested.DictSize(), 6ul);
-  ASSERT_EQ(*nested.FindStringPath("message"), "Message");
-  ASSERT_EQ(nested.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(nested.FindListPath("causes")->GetListDeprecated().size(), 0ul);
-  ASSERT_EQ(nested.FindDictPath("data")->DictSize(), 0ul);
+  base::Value* nested = actual.FindDictPath("cause");
+  ASSERT_NE(nested, nullptr);
+  ASSERT_EQ(nested->DictSize(), 5ul);
+  ASSERT_EQ(*nested->FindStringPath("message"), "Message");
+  ASSERT_EQ(nested->FindListPath("stack")->GetListDeprecated().size(), 1ul);
+  ASSERT_EQ(nested->FindKey("cause"), nullptr);
+  ASSERT_EQ(nested->FindDictPath("data")->DictSize(), 0ul);
 }
 
 TEST_F(StatusTest, CausedByCanAssignCopy) {
@@ -232,21 +234,19 @@
   base::Value causal_serialized = MediaSerialize(causal);
   base::Value copy_causal_serialized = MediaSerialize(copy_causal);
 
-  base::Value& original =
-      causal_serialized.FindListPath("causes")->GetListDeprecated()[0];
-  ASSERT_EQ(original.DictSize(), 6ul);
-  ASSERT_EQ(*original.FindStringPath("message"), "Message");
-  ASSERT_EQ(original.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(original.FindListPath("causes")->GetListDeprecated().size(), 0ul);
-  ASSERT_EQ(original.FindDictPath("data")->DictSize(), 0ul);
+  base::Value* original = causal_serialized.FindDictPath("cause");
+  ASSERT_EQ(original->DictSize(), 5ul);
+  ASSERT_EQ(*original->FindStringPath("message"), "Message");
+  ASSERT_EQ(original->FindListPath("stack")->GetListDeprecated().size(), 1ul);
+  ASSERT_EQ(original->FindKey("cause"), nullptr);
+  ASSERT_EQ(original->FindDictPath("data")->DictSize(), 0ul);
 
-  base::Value& copied =
-      copy_causal_serialized.FindListPath("causes")->GetListDeprecated()[0];
-  ASSERT_EQ(copied.DictSize(), 6ul);
-  ASSERT_EQ(*copied.FindStringPath("message"), "Message");
-  ASSERT_EQ(copied.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(copied.FindListPath("causes")->GetListDeprecated().size(), 0ul);
-  ASSERT_EQ(copied.FindDictPath("data")->DictSize(), 0ul);
+  base::Value* copied = copy_causal_serialized.FindDictPath("cause");
+  ASSERT_EQ(copied->DictSize(), 5ul);
+  ASSERT_EQ(*copied->FindStringPath("message"), "Message");
+  ASSERT_EQ(copied->FindListPath("stack")->GetListDeprecated().size(), 1ul);
+  ASSERT_EQ(copied->FindKey("cause"), nullptr);
+  ASSERT_EQ(copied->FindDictPath("data")->DictSize(), 0ul);
 }
 
 TEST_F(StatusTest, CanCopyEasily) {
@@ -254,17 +254,17 @@
   NormalStatus withData = DoSomethingGiveItBack(failed);
 
   base::Value actual = MediaSerialize(failed);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul);
 
   actual = MediaSerialize(withData);
-  ASSERT_EQ(actual.DictSize(), 6ul);
+  ASSERT_EQ(actual.DictSize(), 5ul);
   ASSERT_EQ(*actual.FindStringPath("message"), "Message");
   ASSERT_EQ(actual.FindListPath("stack")->GetListDeprecated().size(), 1ul);
-  ASSERT_EQ(actual.FindListPath("causes")->GetListDeprecated().size(), 0ul);
+  ASSERT_EQ(actual.FindKey("cause"), nullptr);
   ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul);
 }
 
diff --git a/media/filters/frame_processor.cc b/media/filters/frame_processor.cc
index 3f99a699..4e7f622e6 100644
--- a/media/filters/frame_processor.cc
+++ b/media/filters/frame_processor.cc
@@ -804,11 +804,11 @@
                                    << " frame";
       return false;
     }
-    if (decode_timestamp == kNoDecodeTimestamp) {
-      MEDIA_LOG(ERROR, media_log_) << "Unknown DTS for " << frame->GetTypeName()
-                                   << " frame";
-      return false;
-    }
+
+    // StreamParserBuffer's GetDecodeTimestamp() shouldn't return
+    // kNoDecodeTimestamp if we already found the frame's PTS was kNoTimestamp
+    // and failed processing.
+    DCHECK(decode_timestamp != kNoDecodeTimestamp);
 
     // TODO(wolenetz): Determine whether any DTS>PTS logging is needed. See
     // http://crbug.com/354518.
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
index a60f9b791..e44adfd 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
@@ -132,6 +132,7 @@
   std::vector<uint8_t> output(input.size());
 
   auto status = converter.ConvertChunk(input, output, nullptr, nullptr);
+
   ASSERT_EQ(status.code(), MP4Status::Codes::kInvalidSPS);
 }
 
diff --git a/media/gpu/android/frame_info_helper.cc b/media/gpu/android/frame_info_helper.cc
index 551a9310..d03afda 100644
--- a/media/gpu/android/frame_info_helper.cc
+++ b/media/gpu/android/frame_info_helper.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/android/frame_info_helper.h"
 
+#include "base/debug/crash_logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/threading/sequence_bound.h"
 #include "gpu/command_buffer/service/shared_image_video.h"
@@ -76,7 +77,8 @@
         std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
         base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
                                 absl::optional<FrameInfo>)> cb,
-        std::unique_ptr<base::AutoLockMaybe> scoped_drdc_lock) {
+        std::unique_ptr<base::AutoLockMaybe> scoped_drdc_lock,
+        std::unique_ptr<base::debug::ScopedCrashKeyString> scoped_crash_key) {
       DCHECK(buffer_renderer);
 
       auto texture_owner = buffer_renderer->texture_owner();
@@ -98,6 +100,7 @@
       }
       // Release the lock here since we already got the frame info.
       scoped_drdc_lock.reset();
+      scoped_crash_key.reset();
       std::move(cb).Run(std::move(buffer_renderer), info);
     }
 
@@ -112,15 +115,26 @@
       // GetFrameInfoImpl() ends.
       auto scoped_drdc_lock = std::make_unique<base::AutoLockMaybe>(
           drdc_lock ? drdc_lock->GetDrDcLockPtr() : nullptr);
+
+      // Adding a ScopedCrashKeyString here which will be released when
+      // scoped_drdc_lock is released. Since this is the only place we hold onto
+      // the drdc_lock for longer time than the current call duration,
+      // ScopedCrashKeyString will help to identify if there are any
+      // issues/deadlocks due to this somewhere and if this lock was still held.
+      static auto* kCrashKey = base::debug::AllocateCrashKeyString(
+          "GetFrameInfo_holding_drdc_lock", base::debug::CrashKeySize::Size32);
+      auto scoped_crash_key =
+          std::make_unique<base::debug::ScopedCrashKeyString>(kCrashKey, "1");
+
       DCHECK(buffer_renderer);
 
       auto texture_owner = buffer_renderer->texture_owner();
       DCHECK(texture_owner);
 
-      auto buffer_available_cb =
-          base::BindOnce(&OnGpu::GetFrameInfoImpl, weak_factory_.GetWeakPtr(),
-                         std::move(buffer_renderer), std::move(cb),
-                         std::move(scoped_drdc_lock));
+      auto buffer_available_cb = base::BindOnce(
+          &OnGpu::GetFrameInfoImpl, weak_factory_.GetWeakPtr(),
+          std::move(buffer_renderer), std::move(cb),
+          std::move(scoped_drdc_lock), std::move(scoped_crash_key));
       texture_owner->RunWhenBufferIsAvailable(std::move(buffer_available_cb));
     }
 
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 5d57110..6fd62f1b 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -482,7 +482,7 @@
   uint16 code;
   string message;
   array<mojo_base.mojom.Value> frames;
-  array<StatusData> causes;
+  StatusData? cause;
   mojo_base.mojom.Value data;
 };
 
diff --git a/media/mojo/mojom/status_mojom_traits.cc b/media/mojo/mojom/status_mojom_traits.cc
index 40aeac7..7c88970 100644
--- a/media/mojo/mojom/status_mojom_traits.cc
+++ b/media/mojo/mojom/status_mojom_traits.cc
@@ -28,12 +28,14 @@
   if (!data.ReadData(&output->data))
     return false;
 
-  std::vector<media::internal::StatusData> causes;
-  if (!data.ReadCauses(&causes))
+  absl::optional<media::internal::StatusData> cause;
+  if (!data.ReadCause(&cause))
     return false;
 
-  for (const auto& cause : causes)
-    output->causes.push_back(cause);
+  if (cause.has_value()) {
+    output->cause =
+        std::make_unique<media::internal::StatusData>(std::move(*cause));
+  }
 
   return true;
 }
diff --git a/media/mojo/mojom/status_mojom_traits.h b/media/mojo/mojom/status_mojom_traits.h
index ed8bb28..8572740 100644
--- a/media/mojo/mojom/status_mojom_traits.h
+++ b/media/mojo/mojom/status_mojom_traits.h
@@ -36,9 +36,11 @@
     return input.frames;
   }
 
-  static base::span<media::internal::StatusData> causes(
-      media::internal::StatusData& input) {
-    return input.causes;
+  static absl::optional<media::internal::StatusData> cause(
+      const media::internal::StatusData& input) {
+    if (input.cause)
+      return *input.cause;
+    return absl::nullopt;
   }
 
   static base::Value data(const media::internal::StatusData& input) {
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index be3e91a3..8dfd13c5 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -425,6 +425,12 @@
     if (hint == mojom::WebClientHintsType::kFullUserAgent) {
       continue;
     }
+    // TODO(crbug.com/1225444): `Sec-CH-Partitioned-Cookies` client hint isn't
+    // yet in the request, so we skip over it to avoid triggering a restart.
+    // Remove this line when support is added for `Sec-CH-Partitioned-Cookies`.
+    if (hint == mojom::WebClientHintsType::kPartitionedCookies) {
+      continue;
+    }
 
     const std::string header = GetClientHintToNameMap().at(hint);
     if (!headers.HasHeader(header))
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9d44f58..3f641fa7 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -254,6 +254,9 @@
             "experiments": [
                 {
                     "name": "Enabled_10000_2022-02-01",
+                    "params": {
+                        "save_password_message_dismiss_duration_ms": "10000"
+                    },
                     "enable_features": [
                         "MessagesForAndroidPasswords",
                         "MessagesForAndroidUpdatePassword"
@@ -4312,6 +4315,40 @@
             ]
         }
     ],
+    "OmniboxPrerender": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledPrerender_20220125",
+                    "enable_features": [
+                        "OmniboxTriggerForPrerender2"
+                    ],
+                    "disable_features": [
+                        "OmniboxTriggerForNoStatePrefetch"
+                    ]
+                },
+                {
+                    "name": "EnabledPrefetch_20220125",
+                    "enable_features": [
+                        "OmniboxTriggerForNoStatePrefetch"
+                    ],
+                    "disable_features": [
+                        "OmniboxTriggerForPrerender2"
+                    ]
+                },
+                {
+                    "name": "Disabled_20220125",
+                    "disable_features": [
+                        "OmniboxTriggerForNoStatePrefetch",
+                        "OmniboxTriggerForPrerender2"
+                    ]
+                }
+            ]
+        }
+    ],
     "OmniboxUpdatedConnectionSecurityIndicatorsIPH": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/webid/federated_auth_request.mojom b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
index 3c1303a..671e3ef 100644
--- a/third_party/blink/public/mojom/webid/federated_auth_request.mojom
+++ b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
@@ -14,6 +14,9 @@
 // be updated alongside FederatedAuthRequestIssueReason in
 // third_party/blink/public/devtools_protocol/browser_protocol.pdl, which
 // includes all cases except for kSuccess.
+// TODO(npm): Refactor RequestIdTokenStatus to only send generic messages to the
+// renderer and have a separate enum that has detailed messages in content/.
+// Don't change the meaning or the order of these values before it's in place.
 enum RequestIdTokenStatus {
   kSuccess,
   kApprovalDeclined,
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 1a9f8b9b..3bde272 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -66,6 +66,28 @@
     case CSSPrimitiveValue::UnitType::kViewportMin:
     case CSSPrimitiveValue::UnitType::kViewportMax:
       return kCalcLength;
+    case CSSPrimitiveValue::UnitType::kViewportInlineSize:
+    case CSSPrimitiveValue::UnitType::kViewportBlockSize:
+    case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
+    case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
+    case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
+    case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
+    case CSSPrimitiveValue::UnitType::kSmallViewportMin:
+    case CSSPrimitiveValue::UnitType::kSmallViewportMax:
+    case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
+    case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
+    case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
+    case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
+    case CSSPrimitiveValue::UnitType::kLargeViewportMin:
+    case CSSPrimitiveValue::UnitType::kLargeViewportMax:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
+      return RuntimeEnabledFeatures::CSSViewportUnits4Enabled() ? kCalcLength
+                                                                : kCalcOther;
     case CSSPrimitiveValue::UnitType::kContainerWidth:
     case CSSPrimitiveValue::UnitType::kContainerHeight:
     case CSSPrimitiveValue::UnitType::kContainerInlineSize:
diff --git a/third_party/blink/renderer/core/css/css_numeric_literal_value.cc b/third_party/blink/renderer/core/css/css_numeric_literal_value.cc
index f8aca66..0d64dba 100644
--- a/third_party/blink/renderer/core/css/css_numeric_literal_value.cc
+++ b/third_party/blink/renderer/core/css/css_numeric_literal_value.cc
@@ -219,8 +219,28 @@
     case UnitType::kFraction:
     case UnitType::kViewportWidth:
     case UnitType::kViewportHeight:
+    case UnitType::kViewportInlineSize:
+    case UnitType::kViewportBlockSize:
     case UnitType::kViewportMin:
     case UnitType::kViewportMax:
+    case UnitType::kSmallViewportWidth:
+    case UnitType::kSmallViewportHeight:
+    case UnitType::kSmallViewportInlineSize:
+    case UnitType::kSmallViewportBlockSize:
+    case UnitType::kSmallViewportMin:
+    case UnitType::kSmallViewportMax:
+    case UnitType::kLargeViewportWidth:
+    case UnitType::kLargeViewportHeight:
+    case UnitType::kLargeViewportInlineSize:
+    case UnitType::kLargeViewportBlockSize:
+    case UnitType::kLargeViewportMin:
+    case UnitType::kLargeViewportMax:
+    case UnitType::kDynamicViewportWidth:
+    case UnitType::kDynamicViewportHeight:
+    case UnitType::kDynamicViewportInlineSize:
+    case UnitType::kDynamicViewportBlockSize:
+    case UnitType::kDynamicViewportMin:
+    case UnitType::kDynamicViewportMax:
     case UnitType::kContainerWidth:
     case UnitType::kContainerHeight:
     case UnitType::kContainerInlineSize:
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc
index 155455d..2308417d 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -498,12 +498,72 @@
     case CSSPrimitiveValue::UnitType::kViewportHeight:
       length_type = kUnitTypeViewportHeight;
       return true;
+    case CSSPrimitiveValue::UnitType::kViewportInlineSize:
+      length_type = kUnitTypeViewportInlineSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kViewportBlockSize:
+      length_type = kUnitTypeViewportBlockSize;
+      return true;
     case CSSPrimitiveValue::UnitType::kViewportMin:
       length_type = kUnitTypeViewportMin;
       return true;
     case CSSPrimitiveValue::UnitType::kViewportMax:
       length_type = kUnitTypeViewportMax;
       return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
+      length_type = kUnitTypeSmallViewportWidth;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
+      length_type = kUnitTypeSmallViewportHeight;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
+      length_type = kUnitTypeSmallViewportInlineSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
+      length_type = kUnitTypeSmallViewportBlockSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportMin:
+      length_type = kUnitTypeSmallViewportMin;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportMax:
+      length_type = kUnitTypeSmallViewportMax;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
+      length_type = kUnitTypeLargeViewportWidth;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
+      length_type = kUnitTypeLargeViewportHeight;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
+      length_type = kUnitTypeLargeViewportInlineSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
+      length_type = kUnitTypeLargeViewportBlockSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportMin:
+      length_type = kUnitTypeLargeViewportMin;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportMax:
+      length_type = kUnitTypeLargeViewportMax;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
+      length_type = kUnitTypeDynamicViewportWidth;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
+      length_type = kUnitTypeDynamicViewportHeight;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
+      length_type = kUnitTypeDynamicViewportInlineSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
+      length_type = kUnitTypeDynamicViewportBlockSize;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
+      length_type = kUnitTypeDynamicViewportMin;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
+      length_type = kUnitTypeDynamicViewportMax;
+      return true;
     case CSSPrimitiveValue::UnitType::kContainerWidth:
       length_type = kUnitTypeContainerWidth;
       return true;
@@ -546,10 +606,50 @@
       return CSSPrimitiveValue::UnitType::kViewportWidth;
     case kUnitTypeViewportHeight:
       return CSSPrimitiveValue::UnitType::kViewportHeight;
+    case kUnitTypeViewportInlineSize:
+      return CSSPrimitiveValue::UnitType::kViewportInlineSize;
+    case kUnitTypeViewportBlockSize:
+      return CSSPrimitiveValue::UnitType::kViewportBlockSize;
     case kUnitTypeViewportMin:
       return CSSPrimitiveValue::UnitType::kViewportMin;
     case kUnitTypeViewportMax:
       return CSSPrimitiveValue::UnitType::kViewportMax;
+    case kUnitTypeSmallViewportWidth:
+      return CSSPrimitiveValue::UnitType::kSmallViewportWidth;
+    case kUnitTypeSmallViewportHeight:
+      return CSSPrimitiveValue::UnitType::kSmallViewportHeight;
+    case kUnitTypeSmallViewportInlineSize:
+      return CSSPrimitiveValue::UnitType::kSmallViewportInlineSize;
+    case kUnitTypeSmallViewportBlockSize:
+      return CSSPrimitiveValue::UnitType::kSmallViewportBlockSize;
+    case kUnitTypeSmallViewportMin:
+      return CSSPrimitiveValue::UnitType::kSmallViewportMin;
+    case kUnitTypeSmallViewportMax:
+      return CSSPrimitiveValue::UnitType::kSmallViewportMax;
+    case kUnitTypeLargeViewportWidth:
+      return CSSPrimitiveValue::UnitType::kLargeViewportWidth;
+    case kUnitTypeLargeViewportHeight:
+      return CSSPrimitiveValue::UnitType::kLargeViewportHeight;
+    case kUnitTypeLargeViewportInlineSize:
+      return CSSPrimitiveValue::UnitType::kLargeViewportInlineSize;
+    case kUnitTypeLargeViewportBlockSize:
+      return CSSPrimitiveValue::UnitType::kLargeViewportBlockSize;
+    case kUnitTypeLargeViewportMin:
+      return CSSPrimitiveValue::UnitType::kLargeViewportMin;
+    case kUnitTypeLargeViewportMax:
+      return CSSPrimitiveValue::UnitType::kLargeViewportMax;
+    case kUnitTypeDynamicViewportWidth:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportWidth;
+    case kUnitTypeDynamicViewportHeight:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportHeight;
+    case kUnitTypeDynamicViewportInlineSize:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize;
+    case kUnitTypeDynamicViewportBlockSize:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize;
+    case kUnitTypeDynamicViewportMin:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportMin;
+    case kUnitTypeDynamicViewportMax:
+      return CSSPrimitiveValue::UnitType::kDynamicViewportMax;
     case kUnitTypeContainerWidth:
       return CSSPrimitiveValue::UnitType::kContainerWidth;
     case kUnitTypeContainerHeight:
@@ -628,10 +728,50 @@
       return "vw";
     case UnitType::kViewportHeight:
       return "vh";
+    case UnitType::kViewportInlineSize:
+      return "vi";
+    case UnitType::kViewportBlockSize:
+      return "vb";
     case UnitType::kViewportMin:
       return "vmin";
     case UnitType::kViewportMax:
       return "vmax";
+    case UnitType::kSmallViewportWidth:
+      return "svw";
+    case UnitType::kSmallViewportHeight:
+      return "svh";
+    case UnitType::kSmallViewportInlineSize:
+      return "svi";
+    case UnitType::kSmallViewportBlockSize:
+      return "svb";
+    case UnitType::kSmallViewportMin:
+      return "svmin";
+    case UnitType::kSmallViewportMax:
+      return "svmax";
+    case UnitType::kLargeViewportWidth:
+      return "lvw";
+    case UnitType::kLargeViewportHeight:
+      return "lvh";
+    case UnitType::kLargeViewportInlineSize:
+      return "lvi";
+    case UnitType::kLargeViewportBlockSize:
+      return "lvb";
+    case UnitType::kLargeViewportMin:
+      return "lvmin";
+    case UnitType::kLargeViewportMax:
+      return "lvmax";
+    case UnitType::kDynamicViewportWidth:
+      return "dvw";
+    case UnitType::kDynamicViewportHeight:
+      return "dvh";
+    case UnitType::kDynamicViewportInlineSize:
+      return "dvi";
+    case UnitType::kDynamicViewportBlockSize:
+      return "dvb";
+    case UnitType::kDynamicViewportMin:
+      return "dvmin";
+    case UnitType::kDynamicViewportMax:
+      return "dvmax";
     case UnitType::kContainerWidth:
       return "qw";
     case UnitType::kContainerHeight:
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h
index fab4664..a4702185 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.h
+++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -79,16 +79,45 @@
     kPoints,
     kPicas,
     kQuarterMillimeters,
+
+    // https://drafts.csswg.org/css-values-4/#viewport-relative-lengths
+    //
+    // See also IsViewportPercentageLength.
     kViewportWidth,
     kViewportHeight,
+    kViewportInlineSize,
+    kViewportBlockSize,
     kViewportMin,
     kViewportMax,
+    kSmallViewportWidth,
+    kSmallViewportHeight,
+    kSmallViewportInlineSize,
+    kSmallViewportBlockSize,
+    kSmallViewportMin,
+    kSmallViewportMax,
+    kLargeViewportWidth,
+    kLargeViewportHeight,
+    kLargeViewportInlineSize,
+    kLargeViewportBlockSize,
+    kLargeViewportMin,
+    kLargeViewportMax,
+    kDynamicViewportWidth,
+    kDynamicViewportHeight,
+    kDynamicViewportInlineSize,
+    kDynamicViewportBlockSize,
+    kDynamicViewportMin,
+    kDynamicViewportMax,
+
+    // https://drafts.csswg.org/css-contain-3/#container-lengths
+    //
+    // See also IsContainerPercentageLength.
     kContainerWidth,
     kContainerHeight,
     kContainerInlineSize,
     kContainerBlockSize,
     kContainerMin,
     kContainerMax,
+
     kRems,
     kChs,
     kUserUnits,  // The SVG term for unitless lengths
@@ -128,8 +157,28 @@
     kUnitTypeZeroCharacterWidth,
     kUnitTypeViewportWidth,
     kUnitTypeViewportHeight,
+    kUnitTypeViewportInlineSize,
+    kUnitTypeViewportBlockSize,
     kUnitTypeViewportMin,
     kUnitTypeViewportMax,
+    kUnitTypeSmallViewportWidth,
+    kUnitTypeSmallViewportHeight,
+    kUnitTypeSmallViewportInlineSize,
+    kUnitTypeSmallViewportBlockSize,
+    kUnitTypeSmallViewportMin,
+    kUnitTypeSmallViewportMax,
+    kUnitTypeLargeViewportWidth,
+    kUnitTypeLargeViewportHeight,
+    kUnitTypeLargeViewportInlineSize,
+    kUnitTypeLargeViewportBlockSize,
+    kUnitTypeLargeViewportMin,
+    kUnitTypeLargeViewportMax,
+    kUnitTypeDynamicViewportWidth,
+    kUnitTypeDynamicViewportHeight,
+    kUnitTypeDynamicViewportInlineSize,
+    kUnitTypeDynamicViewportBlockSize,
+    kUnitTypeDynamicViewportMin,
+    kUnitTypeDynamicViewportMax,
     kUnitTypeContainerWidth,
     kUnitTypeContainerHeight,
     kUnitTypeContainerInlineSize,
@@ -182,7 +231,8 @@
   }
   bool IsAngle() const;
   static bool IsViewportPercentageLength(UnitType type) {
-    return type >= UnitType::kViewportWidth && type <= UnitType::kViewportMax;
+    return type >= UnitType::kViewportWidth &&
+           type <= UnitType::kDynamicViewportMax;
   }
   static bool IsContainerPercentageLength(UnitType type) {
     return type >= UnitType::kContainerWidth && type <= UnitType::kContainerMax;
diff --git a/third_party/blink/renderer/core/css/css_primitive_value_units.json5 b/third_party/blink/renderer/core/css/css_primitive_value_units.json5
index 8b9f1c38..b71f66dd 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value_units.json5
+++ b/third_party/blink/renderer/core/css/css_primitive_value_units.json5
@@ -95,6 +95,14 @@
       unit_type: "kViewportHeight",
     },
     {
+      name: "vi",
+      unit_type: "kViewportInlineSize",
+    },
+    {
+      name: "vb",
+      unit_type: "kViewportBlockSize",
+    },
+    {
       name: "vmin",
       unit_type: "kViewportMin",
     },
@@ -103,6 +111,78 @@
       unit_type: "kViewportMax",
     },
     {
+      name: "svw",
+      unit_type: "kSmallViewportWidth",
+    },
+    {
+      name: "svh",
+      unit_type: "kSmallViewportHeight",
+    },
+    {
+      name: "svi",
+      unit_type: "kSmallViewportInlineSize",
+    },
+    {
+      name: "svb",
+      unit_type: "kSmallViewportBlockSize",
+    },
+    {
+      name: "svmin",
+      unit_type: "kSmallViewportMin",
+    },
+    {
+      name: "svmax",
+      unit_type: "kSmallViewportMax",
+    },
+    {
+      name: "lvw",
+      unit_type: "kLargeViewportWidth",
+    },
+    {
+      name: "lvh",
+      unit_type: "kLargeViewportHeight",
+    },
+    {
+      name: "lvi",
+      unit_type: "kLargeViewportInlineSize",
+    },
+    {
+      name: "lvb",
+      unit_type: "kLargeViewportBlockSize",
+    },
+    {
+      name: "lvmin",
+      unit_type: "kLargeViewportMin",
+    },
+    {
+      name: "lvmax",
+      unit_type: "kLargeViewportMax",
+    },
+    {
+      name: "dvw",
+      unit_type: "kDynamicViewportWidth",
+    },
+    {
+      name: "dvh",
+      unit_type: "kDynamicViewportHeight",
+    },
+    {
+      name: "dvi",
+      unit_type: "kDynamicViewportInlineSize",
+    },
+    {
+      name: "dvb",
+      unit_type: "kDynamicViewportBlockSize",
+    },
+    {
+      name: "dvmin",
+      unit_type: "kDynamicViewportMin",
+    },
+    {
+      name: "dvmax",
+      unit_type: "kDynamicViewportMax",
+    },
+    {
       name: "qw",
       unit_type: "kContainerWidth",
     },
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
index a6170754..2e9dcdc 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
@@ -112,9 +112,18 @@
 CSSToLengthConversionData::ViewportSize::ViewportSize(
     const LayoutView* layout_view) {
   if (layout_view) {
-    gfx::SizeF size = layout_view->ViewportSizeForViewportUnits();
-    width_ = size.width();
-    height_ = size.height();
+    gfx::SizeF large_size = layout_view->LargeViewportSizeForViewportUnits();
+    large_width_ = large_size.width();
+    large_height_ = large_size.height();
+
+    gfx::SizeF small_size = layout_view->SmallViewportSizeForViewportUnits();
+    small_width_ = small_size.width();
+    small_height_ = small_size.height();
+
+    gfx::SizeF dynamic_size =
+        layout_view->DynamicViewportSizeForViewportUnits();
+    dynamic_width_ = dynamic_size.width();
+    dynamic_height_ = dynamic_size.height();
   }
 }
 
@@ -179,6 +188,20 @@
     const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
   return viewport_size_.Height() / 100;
 }
+double CSSToLengthConversionData::ViewportInlineSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.Width()
+                                    : viewport_size_.Height()) /
+         100;
+}
+double CSSToLengthConversionData::ViewportBlockSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.Height()
+                                    : viewport_size_.Width()) /
+         100;
+}
 double CSSToLengthConversionData::ViewportMinPercent() const {
   if (style_)
     const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
@@ -190,6 +213,134 @@
   return std::max(viewport_size_.Width(), viewport_size_.Height()) / 100;
 }
 
+double CSSToLengthConversionData::SmallViewportWidthPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.SmallWidth() / 100;
+}
+
+double CSSToLengthConversionData::SmallViewportHeightPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.SmallHeight() / 100;
+}
+
+double CSSToLengthConversionData::SmallViewportInlineSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.SmallWidth()
+                                    : viewport_size_.SmallHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::SmallViewportBlockSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.SmallHeight()
+                                    : viewport_size_.SmallWidth()) /
+         100;
+}
+
+double CSSToLengthConversionData::SmallViewportMinPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::min(viewport_size_.SmallWidth(), viewport_size_.SmallHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::SmallViewportMaxPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::max(viewport_size_.SmallWidth(), viewport_size_.SmallHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::LargeViewportWidthPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.LargeWidth() / 100;
+}
+
+double CSSToLengthConversionData::LargeViewportHeightPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.LargeHeight() / 100;
+}
+
+double CSSToLengthConversionData::LargeViewportInlineSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.LargeWidth()
+                                    : viewport_size_.LargeHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::LargeViewportBlockSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.LargeHeight()
+                                    : viewport_size_.LargeWidth()) /
+         100;
+}
+
+double CSSToLengthConversionData::LargeViewportMinPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::min(viewport_size_.LargeWidth(), viewport_size_.LargeHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::LargeViewportMaxPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::max(viewport_size_.LargeWidth(), viewport_size_.LargeHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::DynamicViewportWidthPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.DynamicWidth() / 100;
+}
+
+double CSSToLengthConversionData::DynamicViewportHeightPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return viewport_size_.DynamicHeight() / 100;
+}
+
+double CSSToLengthConversionData::DynamicViewportInlineSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.DynamicWidth()
+                                    : viewport_size_.DynamicHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::DynamicViewportBlockSizePercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return (IsHorizontalWritingMode() ? viewport_size_.DynamicHeight()
+                                    : viewport_size_.DynamicWidth()) /
+         100;
+}
+
+double CSSToLengthConversionData::DynamicViewportMinPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::min(viewport_size_.DynamicWidth(),
+                  viewport_size_.DynamicHeight()) /
+         100;
+}
+
+double CSSToLengthConversionData::DynamicViewportMaxPercent() const {
+  if (style_)
+    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+  return std::max(viewport_size_.DynamicWidth(),
+                  viewport_size_.DynamicHeight()) /
+         100;
+}
+
 double CSSToLengthConversionData::ContainerWidthPercent() const {
   if (style_)
     const_cast<ComputedStyle*>(style_)->SetHasContainerRelativeUnits();
@@ -286,12 +437,72 @@
     case CSSPrimitiveValue::UnitType::kViewportHeight:
       return value * ViewportHeightPercent() * Zoom();
 
+    case CSSPrimitiveValue::UnitType::kViewportInlineSize:
+      return value * ViewportInlineSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kViewportBlockSize:
+      return value * ViewportBlockSizePercent() * Zoom();
+
     case CSSPrimitiveValue::UnitType::kViewportMin:
       return value * ViewportMinPercent() * Zoom();
 
     case CSSPrimitiveValue::UnitType::kViewportMax:
       return value * ViewportMaxPercent() * Zoom();
 
+    case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
+      return value * SmallViewportWidthPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
+      return value * SmallViewportHeightPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
+      return value * SmallViewportInlineSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
+      return value * SmallViewportBlockSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kSmallViewportMin:
+      return value * SmallViewportMinPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kSmallViewportMax:
+      return value * SmallViewportMaxPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
+      return value * LargeViewportWidthPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
+      return value * LargeViewportHeightPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
+      return value * LargeViewportInlineSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
+      return value * LargeViewportBlockSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportMin:
+      return value * LargeViewportMinPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kLargeViewportMax:
+      return value * LargeViewportMaxPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
+      return value * DynamicViewportWidthPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
+      return value * DynamicViewportHeightPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
+      return value * DynamicViewportInlineSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
+      return value * DynamicViewportBlockSizePercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
+      return value * DynamicViewportMinPercent() * Zoom();
+
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
+      return value * DynamicViewportMaxPercent() * Zoom();
+
     case CSSPrimitiveValue::UnitType::kContainerWidth:
       return value * ContainerWidthPercent() * Zoom();
 
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
index 021aa4b..a97db66 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
@@ -78,15 +78,41 @@
    public:
     ViewportSize() = default;
     ViewportSize(double width, double height)
-        : width_(width), height_(height) {}
+        : large_width_(width),
+          large_height_(height),
+          small_width_(width),
+          small_height_(height),
+          dynamic_width_(width),
+          dynamic_height_(height) {}
+
     explicit ViewportSize(const LayoutView*);
 
-    double Width() const { return width_; }
-    double Height() const { return height_; }
+    // v*
+    double Width() const { return LargeWidth(); }
+    double Height() const { return LargeHeight(); }
+
+    // lv*
+    double LargeWidth() const { return large_width_; }
+    double LargeHeight() const { return large_height_; }
+
+    // sv*
+    double SmallWidth() const { return small_width_; }
+    double SmallHeight() const { return small_height_; }
+
+    // dv*
+    double DynamicWidth() const { return dynamic_width_; }
+    double DynamicHeight() const { return dynamic_height_; }
 
    private:
-    double width_ = 0;
-    double height_ = 0;
+    // v*, lv*
+    double large_width_ = 0;
+    double large_height_ = 0;
+    // sv*
+    double small_width_ = 0;
+    double small_height_ = 0;
+    // dv*
+    double dynamic_width_ = 0;
+    double dynamic_height_ = 0;
   };
 
   class CORE_EXPORT ContainerSizes {
@@ -133,8 +159,28 @@
   // Accessing these marks the style as having viewport units
   double ViewportWidthPercent() const;
   double ViewportHeightPercent() const;
+  double ViewportInlineSizePercent() const;
+  double ViewportBlockSizePercent() const;
   double ViewportMinPercent() const;
   double ViewportMaxPercent() const;
+  double SmallViewportWidthPercent() const;
+  double SmallViewportHeightPercent() const;
+  double SmallViewportInlineSizePercent() const;
+  double SmallViewportBlockSizePercent() const;
+  double SmallViewportMinPercent() const;
+  double SmallViewportMaxPercent() const;
+  double LargeViewportWidthPercent() const;
+  double LargeViewportHeightPercent() const;
+  double LargeViewportInlineSizePercent() const;
+  double LargeViewportBlockSizePercent() const;
+  double LargeViewportMinPercent() const;
+  double LargeViewportMaxPercent() const;
+  double DynamicViewportWidthPercent() const;
+  double DynamicViewportHeightPercent() const;
+  double DynamicViewportInlineSizePercent() const;
+  double DynamicViewportBlockSizePercent() const;
+  double DynamicViewportMinPercent() const;
+  double DynamicViewportMaxPercent() const;
 
   // Accessing these marks the style as having container relative units.
   double ContainerWidthPercent() const;
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value_type.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value_type.cc
index f053f72..8576b649 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value_type.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value_type.cc
@@ -29,8 +29,28 @@
     case UnitType::kUserUnits:
     case UnitType::kViewportWidth:
     case UnitType::kViewportHeight:
+    case UnitType::kViewportInlineSize:
+    case UnitType::kViewportBlockSize:
     case UnitType::kViewportMin:
     case UnitType::kViewportMax:
+    case UnitType::kSmallViewportWidth:
+    case UnitType::kSmallViewportHeight:
+    case UnitType::kSmallViewportInlineSize:
+    case UnitType::kSmallViewportBlockSize:
+    case UnitType::kSmallViewportMin:
+    case UnitType::kSmallViewportMax:
+    case UnitType::kLargeViewportWidth:
+    case UnitType::kLargeViewportHeight:
+    case UnitType::kLargeViewportInlineSize:
+    case UnitType::kLargeViewportBlockSize:
+    case UnitType::kLargeViewportMin:
+    case UnitType::kLargeViewportMax:
+    case UnitType::kDynamicViewportWidth:
+    case UnitType::kDynamicViewportHeight:
+    case UnitType::kDynamicViewportInlineSize:
+    case UnitType::kDynamicViewportBlockSize:
+    case UnitType::kDynamicViewportMin:
+    case UnitType::kDynamicViewportMax:
     case UnitType::kContainerWidth:
     case UnitType::kContainerHeight:
     case UnitType::kContainerInlineSize:
diff --git a/third_party/blink/renderer/core/css/cssom/css_unit_values.h b/third_party/blink/renderer/core/css/cssom/css_unit_values.h
index 4ca380b..f8ffe5e 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unit_values.h
+++ b/third_party/blink/renderer/core/css/cssom/css_unit_values.h
@@ -49,6 +49,116 @@
                                 CSSPrimitiveValue::UnitType::kViewportHeight);
   }
 
+  static CSSUnitValue* vi(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kViewportInlineSize);
+  }
+
+  static CSSUnitValue* vb(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kViewportBlockSize);
+  }
+
+  static CSSUnitValue* vmin(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kViewportMin);
+  }
+
+  static CSSUnitValue* vmax(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kViewportMax);
+  }
+
+  static CSSUnitValue* svw(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kSmallViewportWidth);
+  }
+
+  static CSSUnitValue* svh(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kSmallViewportHeight);
+  }
+
+  static CSSUnitValue* svi(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kSmallViewportInlineSize);
+  }
+
+  static CSSUnitValue* svb(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kSmallViewportBlockSize);
+  }
+
+  static CSSUnitValue* svmin(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kSmallViewportMin);
+  }
+
+  static CSSUnitValue* svmax(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kSmallViewportMax);
+  }
+
+  static CSSUnitValue* lvw(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kLargeViewportWidth);
+  }
+
+  static CSSUnitValue* lvh(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kLargeViewportHeight);
+  }
+
+  static CSSUnitValue* lvi(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kLargeViewportInlineSize);
+  }
+
+  static CSSUnitValue* lvb(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kLargeViewportBlockSize);
+  }
+
+  static CSSUnitValue* lvmin(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kLargeViewportMin);
+  }
+
+  static CSSUnitValue* lvmax(double value) {
+    return CSSUnitValue::Create(value,
+                                CSSPrimitiveValue::UnitType::kLargeViewportMax);
+  }
+
+  static CSSUnitValue* dvw(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportWidth);
+  }
+
+  static CSSUnitValue* dvh(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportHeight);
+  }
+
+  static CSSUnitValue* dvi(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize);
+  }
+
+  static CSSUnitValue* dvb(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize);
+  }
+
+  static CSSUnitValue* dvmin(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportMin);
+  }
+
+  static CSSUnitValue* dvmax(double value) {
+    return CSSUnitValue::Create(
+        value, CSSPrimitiveValue::UnitType::kDynamicViewportMax);
+  }
+
   static CSSUnitValue* qw(double value) {
     return CSSUnitValue::Create(value,
                                 CSSPrimitiveValue::UnitType::kContainerWidth);
@@ -79,16 +189,6 @@
                                 CSSPrimitiveValue::UnitType::kContainerMax);
   }
 
-  static CSSUnitValue* vmin(double value) {
-    return CSSUnitValue::Create(value,
-                                CSSPrimitiveValue::UnitType::kViewportMin);
-  }
-
-  static CSSUnitValue* vmax(double value) {
-    return CSSUnitValue::Create(value,
-                                CSSPrimitiveValue::UnitType::kViewportMax);
-  }
-
   static CSSUnitValue* cm(double value) {
     return CSSUnitValue::Create(value,
                                 CSSPrimitiveValue::UnitType::kCentimeters);
diff --git a/third_party/blink/renderer/core/css/cssom/css_unit_values.idl b/third_party/blink/renderer/core/css/cssom/css_unit_values.idl
index 4e51cde..8ec1ad1f 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unit_values.idl
+++ b/third_party/blink/renderer/core/css/cssom/css_unit_values.idl
@@ -17,9 +17,32 @@
   [NewObject] CSSUnitValue rem(double value);
   [NewObject] CSSUnitValue vw(double value);
   [NewObject] CSSUnitValue vh(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue vi(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue vb(double value);
   [NewObject] CSSUnitValue vmin(double value);
   [NewObject] CSSUnitValue vmax(double value);
 
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svw(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svh(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svi(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svb(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svmin(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue svmax(double value);
+
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvw(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvh(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvi(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvb(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvmin(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue lvmax(double value);
+
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvw(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvh(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvi(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvb(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvmin(double value);
+  [NewObject, RuntimeEnabled=CSSViewportUnits4] CSSUnitValue dvmax(double value);
+
   [NewObject, RuntimeEnabled=CSSContainerRelativeUnits] CSSUnitValue qw(double value);
   [NewObject, RuntimeEnabled=CSSContainerRelativeUnits] CSSUnitValue qh(double value);
   [NewObject, RuntimeEnabled=CSSContainerRelativeUnits] CSSUnitValue qi(double value);
@@ -38,8 +61,6 @@
   // [NewObject] CSSUnitValue ic(double value);
   // [NewObject] CSSUnitValue lh(double value);
   // [NewObject] CSSUnitValue rlh(double value);
-  // [NewObject] CSSUnitValue vi(double value);
-  // [NewObject] CSSUnitValue vb(double value);
 
   // <angle>
   [NewObject] CSSUnitValue deg(double value);
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index d304099..da39d1b 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -86,6 +86,46 @@
   return MakeGarbageCollected<MediaValuesCached>();
 }
 
+double MediaValues::ViewportInlineSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? ViewportWidth()
+                                                   : ViewportHeight();
+}
+
+double MediaValues::ViewportBlockSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? ViewportHeight()
+                                                   : ViewportWidth();
+}
+
+double MediaValues::SmallViewportInlineSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? SmallViewportWidth()
+                                                   : SmallViewportHeight();
+}
+
+double MediaValues::SmallViewportBlockSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? SmallViewportHeight()
+                                                   : SmallViewportWidth();
+}
+
+double MediaValues::LargeViewportInlineSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? LargeViewportWidth()
+                                                   : LargeViewportHeight();
+}
+
+double MediaValues::LargeViewportBlockSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? LargeViewportHeight()
+                                                   : LargeViewportWidth();
+}
+
+double MediaValues::DynamicViewportInlineSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? DynamicViewportWidth()
+                                                   : DynamicViewportHeight();
+}
+
+double MediaValues::DynamicViewportBlockSize() const {
+  return IsHorizontalWritingMode(GetWritingMode()) ? DynamicViewportHeight()
+                                                   : DynamicViewportWidth();
+}
+
 double MediaValues::CalculateViewportWidth(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->View());
@@ -100,6 +140,48 @@
   return frame->View()->ViewportSizeForMediaQueries().height();
 }
 
+double MediaValues::CalculateSmallViewportWidth(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->SmallViewportSizeForViewportUnits().width();
+}
+
+double MediaValues::CalculateSmallViewportHeight(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->SmallViewportSizeForViewportUnits().height();
+}
+
+double MediaValues::CalculateLargeViewportWidth(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->LargeViewportSizeForViewportUnits().width();
+}
+
+double MediaValues::CalculateLargeViewportHeight(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->LargeViewportSizeForViewportUnits().height();
+}
+
+double MediaValues::CalculateDynamicViewportWidth(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->DynamicViewportSizeForViewportUnits().width();
+}
+
+double MediaValues::CalculateDynamicViewportHeight(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->View());
+  DCHECK(frame->GetDocument());
+  return frame->View()->DynamicViewportSizeForViewportUnits().height();
+}
+
 int MediaValues::CalculateDeviceWidth(LocalFrame* frame) {
   DCHECK(frame && frame->View() && frame->GetSettings() && frame->GetPage());
   const display::ScreenInfo& screen_info =
@@ -410,12 +492,80 @@
     case CSSPrimitiveValue::UnitType::kViewportHeight:
       result = (value * ViewportHeight()) / 100.0;
       return true;
+    case CSSPrimitiveValue::UnitType::kViewportInlineSize:
+      result = (value * ViewportInlineSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kViewportBlockSize:
+      result = (value * ViewportBlockSize()) / 100.0;
+      return true;
     case CSSPrimitiveValue::UnitType::kViewportMin:
       result = (value * std::min(ViewportWidth(), ViewportHeight())) / 100.0;
       return true;
     case CSSPrimitiveValue::UnitType::kViewportMax:
       result = (value * std::max(ViewportWidth(), ViewportHeight())) / 100.0;
       return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
+      result = (value * SmallViewportWidth()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
+      result = (value * SmallViewportHeight()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
+      result = (value * SmallViewportInlineSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
+      result = (value * SmallViewportBlockSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportMin:
+      result = (value * std::min(SmallViewportWidth(), SmallViewportHeight())) /
+               100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kSmallViewportMax:
+      result = (value * std::max(SmallViewportWidth(), SmallViewportHeight())) /
+               100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
+      result = (value * LargeViewportWidth()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
+      result = (value * LargeViewportHeight()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
+      result = (value * LargeViewportInlineSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
+      result = (value * LargeViewportBlockSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportMin:
+      result = (value * std::min(LargeViewportWidth(), LargeViewportHeight())) /
+               100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kLargeViewportMax:
+      result = (value * std::max(LargeViewportWidth(), LargeViewportHeight())) /
+               100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
+      result = (value * DynamicViewportWidth()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
+      result = (value * DynamicViewportHeight()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
+      result = (value * DynamicViewportInlineSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
+      result = (value * DynamicViewportBlockSize()) / 100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
+      result =
+          (value * std::min(DynamicViewportWidth(), DynamicViewportHeight())) /
+          100.0;
+      return true;
+    case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
+      result =
+          (value * std::max(DynamicViewportWidth(), DynamicViewportHeight())) /
+          100.0;
+      return true;
     case CSSPrimitiveValue::UnitType::kCentimeters:
       result = value * kCssPixelsPerCentimeter;
       return true;
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index 798bd17..ca2783b8 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -86,14 +86,35 @@
  protected:
   virtual double ViewportWidth() const = 0;
   virtual double ViewportHeight() const = 0;
+  virtual double SmallViewportWidth() const = 0;
+  virtual double SmallViewportHeight() const = 0;
+  virtual double LargeViewportWidth() const = 0;
+  virtual double LargeViewportHeight() const = 0;
+  virtual double DynamicViewportWidth() const = 0;
+  virtual double DynamicViewportHeight() const = 0;
   virtual float EmSize() const = 0;
   virtual float RemSize() const = 0;
   virtual float ExSize() const = 0;
   virtual float ChSize() const = 0;
   virtual WritingMode GetWritingMode() const = 0;
 
+  double ViewportInlineSize() const;
+  double ViewportBlockSize() const;
+  double SmallViewportInlineSize() const;
+  double SmallViewportBlockSize() const;
+  double LargeViewportInlineSize() const;
+  double LargeViewportBlockSize() const;
+  double DynamicViewportInlineSize() const;
+  double DynamicViewportBlockSize() const;
+
   static double CalculateViewportWidth(LocalFrame*);
   static double CalculateViewportHeight(LocalFrame*);
+  static double CalculateSmallViewportWidth(LocalFrame*);
+  static double CalculateSmallViewportHeight(LocalFrame*);
+  static double CalculateLargeViewportWidth(LocalFrame*);
+  static double CalculateLargeViewportHeight(LocalFrame*);
+  static double CalculateDynamicViewportWidth(LocalFrame*);
+  static double CalculateDynamicViewportHeight(LocalFrame*);
   static float CalculateEmSize(LocalFrame*);
   static float CalculateExSize(LocalFrame*);
   static float CalculateChSize(LocalFrame*);
diff --git a/third_party/blink/renderer/core/css/media_values_cached.cc b/third_party/blink/renderer/core/css/media_values_cached.cc
index ebbdd78..47e9e4a7 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.cc
+++ b/third_party/blink/renderer/core/css/media_values_cached.cc
@@ -28,6 +28,13 @@
     // MediaValuesCachedData values.
     viewport_width = MediaValues::CalculateViewportWidth(frame);
     viewport_height = MediaValues::CalculateViewportHeight(frame);
+    small_viewport_width = MediaValues::CalculateSmallViewportWidth(frame);
+    small_viewport_height = MediaValues::CalculateSmallViewportHeight(frame);
+    large_viewport_width = MediaValues::CalculateLargeViewportWidth(frame);
+    large_viewport_height = MediaValues::CalculateLargeViewportHeight(frame);
+    dynamic_viewport_width = MediaValues::CalculateDynamicViewportWidth(frame);
+    dynamic_viewport_height =
+        MediaValues::CalculateDynamicViewportHeight(frame);
     device_width = MediaValues::CalculateDeviceWidth(frame);
     device_height = MediaValues::CalculateDeviceHeight(frame);
     device_pixel_ratio = MediaValues::CalculateDevicePixelRatio(frame);
@@ -91,6 +98,30 @@
   return data_.viewport_height;
 }
 
+double MediaValuesCached::SmallViewportWidth() const {
+  return data_.small_viewport_width;
+}
+
+double MediaValuesCached::SmallViewportHeight() const {
+  return data_.small_viewport_height;
+}
+
+double MediaValuesCached::LargeViewportWidth() const {
+  return data_.large_viewport_width;
+}
+
+double MediaValuesCached::LargeViewportHeight() const {
+  return data_.large_viewport_height;
+}
+
+double MediaValuesCached::DynamicViewportWidth() const {
+  return data_.dynamic_viewport_width;
+}
+
+double MediaValuesCached::DynamicViewportHeight() const {
+  return data_.dynamic_viewport_height;
+}
+
 float MediaValuesCached::EmSize() const {
   return data_.em_size;
 }
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 3b201af..c599c307 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -29,6 +29,12 @@
     // thread
     double viewport_width = 0;
     double viewport_height = 0;
+    double small_viewport_width = 0;
+    double small_viewport_height = 0;
+    double large_viewport_width = 0;
+    double large_viewport_height = 0;
+    double dynamic_viewport_width = 0;
+    double dynamic_viewport_height = 0;
     int device_width = 0;
     int device_height = 0;
     float device_pixel_ratio = 1.0;
@@ -144,6 +150,12 @@
  protected:
   double ViewportWidth() const override;
   double ViewportHeight() const override;
+  double SmallViewportWidth() const override;
+  double SmallViewportHeight() const override;
+  double LargeViewportWidth() const override;
+  double LargeViewportHeight() const override;
+  double DynamicViewportWidth() const override;
+  double DynamicViewportHeight() const override;
   float EmSize() const override;
   float RemSize() const override;
   float ExSize() const override;
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.cc b/third_party/blink/renderer/core/css/media_values_dynamic.cc
index 2cb1f9cb..5cf6982 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.cc
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.cc
@@ -57,6 +57,30 @@
   return CalculateViewportHeight(frame_);
 }
 
+double MediaValuesDynamic::SmallViewportWidth() const {
+  return CalculateSmallViewportWidth(frame_);
+}
+
+double MediaValuesDynamic::SmallViewportHeight() const {
+  return CalculateSmallViewportHeight(frame_);
+}
+
+double MediaValuesDynamic::LargeViewportWidth() const {
+  return CalculateLargeViewportWidth(frame_);
+}
+
+double MediaValuesDynamic::LargeViewportHeight() const {
+  return CalculateLargeViewportHeight(frame_);
+}
+
+double MediaValuesDynamic::DynamicViewportWidth() const {
+  return CalculateDynamicViewportWidth(frame_);
+}
+
+double MediaValuesDynamic::DynamicViewportHeight() const {
+  return CalculateDynamicViewportHeight(frame_);
+}
+
 float MediaValuesDynamic::EmSize() const {
   return CalculateEmSize(frame_);
 }
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.h b/third_party/blink/renderer/core/css/media_values_dynamic.h
index 34ee0def..ec607d7 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.h
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.h
@@ -55,6 +55,12 @@
  protected:
   double ViewportWidth() const override;
   double ViewportHeight() const override;
+  double SmallViewportWidth() const override;
+  double SmallViewportHeight() const override;
+  double LargeViewportWidth() const override;
+  double LargeViewportHeight() const override;
+  double DynamicViewportWidth() const override;
+  double DynamicViewportHeight() const override;
   float EmSize() const override;
   float RemSize() const override;
   float ExSize() const override;
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index e2738d6..72ff1a71 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -822,6 +822,29 @@
       case CSSPrimitiveValue::UnitType::kViewportMin:
       case CSSPrimitiveValue::UnitType::kViewportMax:
         break;
+      case CSSPrimitiveValue::UnitType::kViewportInlineSize:
+      case CSSPrimitiveValue::UnitType::kViewportBlockSize:
+      case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
+      case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
+      case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
+      case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
+      case CSSPrimitiveValue::UnitType::kSmallViewportMin:
+      case CSSPrimitiveValue::UnitType::kSmallViewportMax:
+      case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
+      case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
+      case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
+      case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
+      case CSSPrimitiveValue::UnitType::kLargeViewportMin:
+      case CSSPrimitiveValue::UnitType::kLargeViewportMax:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
+      case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
+        if (!RuntimeEnabledFeatures::CSSViewportUnits4Enabled())
+          return nullptr;
+        break;
       case CSSPrimitiveValue::UnitType::kContainerWidth:
       case CSSPrimitiveValue::UnitType::kContainerHeight:
       case CSSPrimitiveValue::UnitType::kContainerInlineSize:
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index fba2c0f9..a2d3549b 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -3963,6 +3963,35 @@
   }
 }
 
+TEST_F(StyleEngineTest, CSSViewportUnits4RuntimeFlag) {
+  Vector<String> units = {"vi",  "vb",    "svi",   "svb",   "svw",
+                          "svh", "svmin", "svmax", "lvi",   "lvb",
+                          "lvw", "lvh",   "lvmin", "lvmax", "dvi",
+                          "dvb", "dvw",   "dvh",   "dvmin", "dvmax"};
+
+  for (const String& unit : units) {
+    String css = "top: 1" + unit;
+    SCOPED_TRACE(testing::Message() << unit);
+
+    {
+      ScopedCSSViewportUnits4ForTest flag(false);
+      const CSSPropertyValueSet* set =
+          css_test_helpers::ParseDeclarationBlock(css);
+      ASSERT_TRUE(set);
+      EXPECT_EQ(0u, set->PropertyCount());
+    }
+
+    {
+      ScopedCSSViewportUnits4ForTest flag(true);
+      const CSSPropertyValueSet* set =
+          css_test_helpers::ParseDeclarationBlock(css);
+      ASSERT_TRUE(set);
+      EXPECT_EQ(1u, set->PropertyCount());
+      EXPECT_TRUE(set->HasProperty(CSSPropertyID::kTop));
+    }
+  }
+}
+
 TEST_F(StyleEngineTest, ContainerPropertiesRuntimeFlag) {
   Vector<String> declarations = {"container-type:inline-size",
                                  "container-name:foo", "container:inline-size"};
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index a98d568b..d7c3ca4a 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4580,6 +4580,21 @@
   SetNeedsStyleRecalcForViewportUnits();
 }
 
+void Document::DynamicViewportUnitsChanged() {
+  if (!RuntimeEnabledFeatures::CSSViewportUnits4Enabled())
+    return;
+  // TODO(crbug.com/1093055): Avoid invalidating media queries if dv* is not
+  // used.
+  MediaQueryAffectingValueChanged(MediaValueChange::kSize);
+  if (media_query_matcher_)
+    media_query_matcher_->ViewportChanged();
+  // TODO(crbug.com/1093055): Target dv* specifically.
+  if (!HasViewportUnits())
+    return;
+  GetStyleResolver().SetResizedForViewportUnits();
+  SetNeedsStyleRecalcForViewportUnits();
+}
+
 void Document::SetHoverElement(Element* new_hover_element) {
   hover_element_ = new_hover_element;
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index f830883..1d58655d 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1486,6 +1486,8 @@
   void SetHasViewportUnits() { has_viewport_units_ = true; }
   bool HasViewportUnits() const { return has_viewport_units_; }
   void LayoutViewportWasResized();
+  // dv*
+  void DynamicViewportUnitsChanged();
 
   void InvalidateStyleAndLayoutForFontUpdates();
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 9ef1ddd..9710d52 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1225,8 +1225,13 @@
     const gfx::Size& visible_viewport_size) {
   DCHECK(MainFrameImpl());
 
+  bool old_viewport_shrink = GetBrowserControls().ShrinkViewport();
+
   GetBrowserControls().SetParams(params);
 
+  if (old_viewport_shrink != GetBrowserControls().ShrinkViewport())
+    MainFrameImpl()->GetFrameView()->DynamicViewportUnitsChanged();
+
   {
     // Avoids unnecessary invalidations while various bits of state in
     // TextAutosizer are updated.
diff --git a/third_party/blink/renderer/core/frame/browser_controls_test.cc b/third_party/blink/renderer/core/frame/browser_controls_test.cc
index 5ba353c..eb2843f 100644
--- a/third_party/blink/renderer/core/frame/browser_controls_test.cc
+++ b/third_party/blink/renderer/core/frame/browser_controls_test.cc
@@ -69,7 +69,10 @@
     RegisterMockedHttpURLLoad("iframe-scrolling.html");
     RegisterMockedHttpURLLoad("iframe-scrolling-inner.html");
     RegisterMockedHttpURLLoad("percent-height.html");
-    RegisterMockedHttpURLLoad("vh-height.html");
+    RegisterMockedHttpURLLoad("v-size.html");
+    RegisterMockedHttpURLLoad("sv-size.html");
+    RegisterMockedHttpURLLoad("lv-size.html");
+    RegisterMockedHttpURLLoad("dv-size.html");
     RegisterMockedHttpURLLoad("vh-height-width-800.html");
     RegisterMockedHttpURLLoad("95-vh.html");
     RegisterMockedHttpURLLoad("vh-height-width-800-extra-wide.html");
@@ -202,6 +205,19 @@
   }
 };
 
+struct ViewportUnitTestCase {
+  // The file to load in the WebView. See mocked files in BrowserControlsTest().
+  const char* filename;
+  // The width that 50*vw should resolve to.
+  float width;
+  // The height that 50*vh should resolve to.
+  float height;
+};
+
+class BrowserControlsViewportUnitTest
+    : public BrowserControlsTest,
+      public ::testing::WithParamInterface<ViewportUnitTestCase> {};
+
 // Disable these tests on Mac OSX until further investigation.
 // Local build on Mac is OK but the bot fails. This is not an issue as
 // Browser Controls are currently only used on Android.
@@ -1021,11 +1037,16 @@
   EXPECT_EQ(400, GetDocument().GetFrame()->View()->GetLayoutSize().height());
 }
 
-// Ensure that browser controls do not affect vh units.
-TEST_F(BrowserControlsTest, MAYBE(DontAffectVHUnits)) {
+// Ensure that browser controls do not affect "static" viewport units
+// (vh, svh, lvh).
+TEST_P(BrowserControlsViewportUnitTest, MAYBE(DontAffectStaticUnits)) {
+  auto param = GetParam();
+  SCOPED_TRACE(param.filename);
+
   // Initialize with the browser controls showing.
-  WebViewImpl* web_view = Initialize("vh-height.html");
-  web_view->ResizeWithBrowserControls(gfx::Size(400, 300), 100.f, 0, true);
+  WebViewImpl* web_view = Initialize(param.filename);
+  web_view->GetPage()->GetChromeClient().SetBrowserControlsState(100.0f, 0.0f,
+                                                                 true);
   web_view->GetBrowserControls().UpdateConstraintsAndState(
       cc::BrowserControlsState::kBoth, cc::BrowserControlsState::kShown);
   web_view->GetBrowserControls().SetShownRatio(1, 1);
@@ -1033,12 +1054,12 @@
 
   ASSERT_EQ(100.f, web_view->GetBrowserControls().ContentOffset());
 
-  // 'vh' units should be based on the viewport when the browser controls are
-  // hidden.
+  // Static '*vh' units should be based on the viewport when the browser
+  // controls are hidden.
   Element* abs_pos = GetElementById(WebString::FromUTF8("abs"));
   Element* fixed_pos = GetElementById(WebString::FromUTF8("fixed"));
-  EXPECT_FLOAT_EQ(200.f, abs_pos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(200.f, fixed_pos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(param.height, abs_pos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(param.height, fixed_pos->getBoundingClientRect()->height());
 
   // The size used for viewport units should not be reduced by the top
   // controls.
@@ -1046,19 +1067,108 @@
 
   // Hide the browser controls.
   VerticalScroll(-100.f);
-  web_view->ResizeWithBrowserControls(gfx::Size(400, 400), 100.f, 0, false);
+  web_view->GetPage()->GetChromeClient().SetBrowserControlsState(100.0f, 0.0f,
+                                                                 false);
   UpdateAllLifecyclePhases();
 
   ASSERT_EQ(0.f, web_view->GetBrowserControls().ContentOffset());
 
-  // vh units should be static with respect to the browser controls so neighter
-  // <div> should change size are a result of the browser controls hiding.
+  // Static *vh units should be static with respect to the browser controls so
+  // neither <div> should change size as a result of the browser controls
+  // hiding.
+  EXPECT_FLOAT_EQ(param.height, abs_pos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(param.height, fixed_pos->getBoundingClientRect()->height());
+
+  // The viewport size used for static *vh units should not change as a result
+  // of top controls hiding.
+  EXPECT_EQ(400, GetFrame()->View()->ViewportSizeForViewportUnits().height());
+
+  // Static *vw units should not change when scrollbar disappears.
+  EXPECT_FLOAT_EQ(param.width, abs_pos->getBoundingClientRect()->width());
+  EXPECT_FLOAT_EQ(param.width, fixed_pos->getBoundingClientRect()->width());
+  Element* spacer = GetElementById(WebString::FromUTF8("spacer"));
+  ASSERT_TRUE(spacer);
+  spacer->remove();
+  UpdateAllLifecyclePhases();
+  EXPECT_FLOAT_EQ(param.width, abs_pos->getBoundingClientRect()->width());
+  EXPECT_FLOAT_EQ(param.width, fixed_pos->getBoundingClientRect()->width());
+}
+
+static ViewportUnitTestCase viewport_unit_test_cases[] = {
+    {"v-size.html", 200.f, 200.f},
+    {"lv-size.html", 200.f, 200.f},
+    {"sv-size.html", 200.f, 150.f},
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         BrowserControlsViewportUnitTest,
+                         testing::ValuesIn(viewport_unit_test_cases));
+
+// Ensure that browser controls *do* affect dvh units.
+TEST_F(BrowserControlsTest, MAYBE(DoAffectDVHUnits)) {
+  // Initialize with the browser controls showing.
+  WebViewImpl* web_view = Initialize("dv-size.html");
+  web_view->GetPage()->GetChromeClient().SetBrowserControlsState(100.0f, 0.0f,
+                                                                 true);
+  web_view->GetBrowserControls().UpdateConstraintsAndState(
+      cc::BrowserControlsState::kBoth, cc::BrowserControlsState::kShown);
+  web_view->GetBrowserControls().SetShownRatio(1, 1);
+  UpdateAllLifecyclePhases();
+
+  ASSERT_EQ(100.f, web_view->GetBrowserControls().ContentOffset());
+
+  // 'dvh' units should respond according to the current state of the controls.
+  Element* abs_pos = GetElementById(WebString::FromUTF8("abs"));
+  Element* fixed_pos = GetElementById(WebString::FromUTF8("fixed"));
+  EXPECT_FLOAT_EQ(150.f, abs_pos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(150.f, fixed_pos->getBoundingClientRect()->height());
+
+  // The size used for viewport units should not be reduced by the top
+  // controls.
+  EXPECT_EQ(300,
+            GetFrame()->View()->DynamicViewportSizeForViewportUnits().height());
+
+  // Hide the browser controls.
+  VerticalScroll(-100.f);
+  web_view->GetPage()->GetChromeClient().SetBrowserControlsState(100.0f, 0.0f,
+                                                                 false);
+  UpdateAllLifecyclePhases();
+
+  ASSERT_EQ(0.f, web_view->GetBrowserControls().ContentOffset());
+
+  // dvh units should be dynamic with respect to the browser controls so both
+  // <div>s should change size as a result of the browser controls hiding.
   EXPECT_FLOAT_EQ(200.f, abs_pos->getBoundingClientRect()->height());
   EXPECT_FLOAT_EQ(200.f, fixed_pos->getBoundingClientRect()->height());
 
-  // The viewport size used for vh units should not change as a result of top
+  // The viewport size used for dvh units should change as a result of top
   // controls hiding.
-  EXPECT_EQ(400, GetFrame()->View()->ViewportSizeForViewportUnits().height());
+  EXPECT_EQ(400,
+            GetFrame()->View()->DynamicViewportSizeForViewportUnits().height());
+
+  // The viewport size used for dvh units should not change as a result of top
+  // controls partially showing.
+  ApplyViewportChangesArgs args;
+  args.page_scale_delta = 1.f;
+  args.is_pinch_gesture_active = false;
+  args.top_controls_delta = 0.5f;
+  args.bottom_controls_delta = 0.f;
+  args.browser_controls_constraint = cc::BrowserControlsState::kBoth;
+  args.scroll_gesture_did_end = false;
+  web_view->ApplyViewportChanges(args);
+  UpdateAllLifecyclePhases();
+  EXPECT_EQ(400,
+            GetFrame()->View()->DynamicViewportSizeForViewportUnits().height());
+
+  // dvw units should not change when scrollbar disappears.
+  EXPECT_FLOAT_EQ(200.f, abs_pos->getBoundingClientRect()->width());
+  EXPECT_FLOAT_EQ(200.f, fixed_pos->getBoundingClientRect()->width());
+  Element* spacer = GetElementById(WebString::FromUTF8("spacer"));
+  ASSERT_TRUE(spacer);
+  spacer->remove();
+  UpdateAllLifecyclePhases();
+  EXPECT_FLOAT_EQ(200.f, abs_pos->getBoundingClientRect()->width());
+  EXPECT_FLOAT_EQ(200.f, fixed_pos->getBoundingClientRect()->width());
 }
 
 // Ensure that on a legacy page (there's a non-1 minimum scale) 100vh units fill
@@ -1140,7 +1250,7 @@
 TEST_F(BrowserControlsTest, MAYBE(VHUnitsWithTopMinHeight)) {
   // Initialize with the browser controls showing.
   // Top controls height: 100, top controls min-height: 20.
-  WebViewImpl* web_view = Initialize("vh-height.html");
+  WebViewImpl* web_view = Initialize("v-size.html");
   web_view->ResizeWithBrowserControls(gfx::Size(400, 300), gfx::Size(400, 300),
                                       {100, 20, 0, 0, false, true});
   web_view->GetBrowserControls().UpdateConstraintsAndState(
@@ -1187,7 +1297,7 @@
   // Initialize with the browser controls showing.
   // Top controls height: 100, top controls min-height: 20.
   // Bottom controls height: 50, bottom controls min-height: 10.
-  WebViewImpl* web_view = Initialize("vh-height.html");
+  WebViewImpl* web_view = Initialize("v-size.html");
   web_view->ResizeWithBrowserControls(gfx::Size(400, 250), gfx::Size(400, 250),
                                       {100, 20, 50, 10, false, true});
   web_view->GetBrowserControls().UpdateConstraintsAndState(
@@ -1234,7 +1344,7 @@
   // Initialize with the browser controls showing.
   // Top controls height: 100, top controls min-height: 20.
   // Bottom controls height: 50, bottom controls min-height: 10.
-  WebViewImpl* web_view = Initialize("vh-height.html");
+  WebViewImpl* web_view = Initialize("v-size.html");
   web_view->ResizeWithBrowserControls(gfx::Size(400, 250), gfx::Size(400, 250),
                                       {100, 20, 50, 10, false, true});
   web_view->GetBrowserControls().UpdateConstraintsAndState(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index fbac2ea..59a28e69 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -955,6 +955,10 @@
 }
 
 gfx::SizeF LocalFrameView::ViewportSizeForViewportUnits() const {
+  return LargeViewportSizeForViewportUnits();
+}
+
+gfx::SizeF LocalFrameView::SmallViewportSizeForViewportUnits() const {
   float zoom = 1;
   if (!frame_->GetDocument() || !frame_->GetDocument()->Printing())
     zoom = GetFrame().PageZoomFactor();
@@ -967,6 +971,16 @@
   layout_size.set_width(layout_view->ViewWidth(kIncludeScrollbars) / zoom);
   layout_size.set_height(layout_view->ViewHeight(kIncludeScrollbars) / zoom);
 
+  return layout_size;
+}
+
+gfx::SizeF LocalFrameView::LargeViewportSizeForViewportUnits() const {
+  auto* layout_view = GetLayoutView();
+  if (!layout_view)
+    return gfx::SizeF();
+
+  gfx::SizeF layout_size = SmallViewportSizeForViewportUnits();
+
   BrowserControls& browser_controls = frame_->GetPage()->GetBrowserControls();
   if (browser_controls.PermittedState() != cc::BrowserControlsState::kHidden) {
     // We use the layoutSize rather than frameRect to calculate viewport units
@@ -978,9 +992,6 @@
     // use the viewport with browser controls hidden for vh (to match Safari).
     int viewport_width = frame_->GetPage()->GetVisualViewport().Size().width();
     if (frame_->IsMainFrame() && layout_size.width() && viewport_width) {
-      // TODO(bokan/eirage): BrowserControl height may need to account for the
-      // zoom factor when use-zoom-for-dsf is enabled on Android. Confirm this
-      // works correctly when that's turned on. https://crbug.com/737777.
       float page_scale_at_layout_width = viewport_width / layout_size.width();
       layout_size.Enlarge(0, (browser_controls.TotalHeight() -
                               browser_controls.TotalMinHeight()) /
@@ -998,6 +1009,13 @@
   return viewport_size;
 }
 
+gfx::SizeF LocalFrameView::DynamicViewportSizeForViewportUnits() const {
+  BrowserControls& browser_controls = frame_->GetPage()->GetBrowserControls();
+  return browser_controls.ShrinkViewport()
+             ? SmallViewportSizeForViewportUnits()
+             : LargeViewportSizeForViewportUnits();
+}
+
 DocumentLifecycle& LocalFrameView::Lifecycle() const {
   DCHECK(frame_);
   DCHECK(frame_->GetDocument());
@@ -1299,6 +1317,11 @@
   }
 }
 
+void LocalFrameView::DynamicViewportUnitsChanged() {
+  if (GetFrame().GetDocument())
+    GetFrame().GetDocument()->DynamicViewportUnitsChanged();
+}
+
 bool LocalFrameView::ShouldSetCursor() const {
   Page* page = GetFrame().GetPage();
   return page && page->IsPageVisible() &&
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index a3a766b..5331e0e 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -286,6 +286,7 @@
   void ViewportSizeChanged(bool width_changed, bool height_changed);
   void MarkViewportConstrainedObjectsForLayout(bool width_changed,
                                                bool height_changed);
+  void DynamicViewportUnitsChanged();
 
   AtomicString MediaType() const;
   void SetMediaType(const AtomicString&);
@@ -620,6 +621,12 @@
   // May include the size of browser controls. See implementation for further
   // documentation.
   gfx::SizeF ViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#small-viewport-size
+  gfx::SizeF SmallViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#large-viewport-size
+  gfx::SizeF LargeViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#dynamic-viewport-size
+  gfx::SizeF DynamicViewportSizeForViewportUnits() const;
 
   // Initial containing block size for evaluating viewport-dependent media
   // queries.
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 6adf1a9..8aa15ecb 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -854,6 +854,24 @@
                         : gfx::SizeF();
 }
 
+gfx::SizeF LayoutView::SmallViewportSizeForViewportUnits() const {
+  NOT_DESTROYED();
+  return GetFrameView() ? GetFrameView()->SmallViewportSizeForViewportUnits()
+                        : gfx::SizeF();
+}
+
+gfx::SizeF LayoutView::LargeViewportSizeForViewportUnits() const {
+  NOT_DESTROYED();
+  return GetFrameView() ? GetFrameView()->LargeViewportSizeForViewportUnits()
+                        : gfx::SizeF();
+}
+
+gfx::SizeF LayoutView::DynamicViewportSizeForViewportUnits() const {
+  NOT_DESTROYED();
+  return GetFrameView() ? GetFrameView()->DynamicViewportSizeForViewportUnits()
+                        : gfx::SizeF();
+}
+
 void LayoutView::WillBeDestroyed() {
   NOT_DESTROYED();
   // TODO(wangxianzhu): This is a workaround of crbug.com/570706.
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 1b3307bc..de147c8 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -287,6 +287,12 @@
   // Returns the viewport size in (CSS pixels) that vh and vw units are
   // calculated from.
   gfx::SizeF ViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#small-viewport-size
+  gfx::SizeF SmallViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#large-viewport-size
+  gfx::SizeF LargeViewportSizeForViewportUnits() const;
+  // https://drafts.csswg.org/css-values-4/#dynamic-viewport-size
+  gfx::SizeF DynamicViewportSizeForViewportUnits() const;
 
   void PushLayoutState(LayoutState& layout_state) {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/testing/data/vh-height.html b/third_party/blink/renderer/core/testing/data/dv-size.html
similarity index 82%
copy from third_party/blink/renderer/core/testing/data/vh-height.html
copy to third_party/blink/renderer/core/testing/data/dv-size.html
index 98807ed..432d4425 100644
--- a/third_party/blink/renderer/core/testing/data/vh-height.html
+++ b/third_party/blink/renderer/core/testing/data/dv-size.html
@@ -8,8 +8,8 @@
       left: 0px;
       top: 0px;
       background: silver;
-      width: 100px;
-      height: 50vh;
+      width: 50dvw;
+      height: 50dvh;
     }
 
     #fixed {
@@ -17,8 +17,8 @@
       right: 0px;
       top: 0px;
       background: red;
-      width: 100px;
-      height: 50vh;
+      width: 50dvw;
+      height: 50dvh;
     }
 
     #spacer {
diff --git a/third_party/blink/renderer/core/testing/data/vh-height.html b/third_party/blink/renderer/core/testing/data/lv-size.html
similarity index 82%
copy from third_party/blink/renderer/core/testing/data/vh-height.html
copy to third_party/blink/renderer/core/testing/data/lv-size.html
index 98807ed..e92e4a57 100644
--- a/third_party/blink/renderer/core/testing/data/vh-height.html
+++ b/third_party/blink/renderer/core/testing/data/lv-size.html
@@ -8,8 +8,8 @@
       left: 0px;
       top: 0px;
       background: silver;
-      width: 100px;
-      height: 50vh;
+      width: 50lvw;
+      height: 50lvh;
     }
 
     #fixed {
@@ -17,8 +17,8 @@
       right: 0px;
       top: 0px;
       background: red;
-      width: 100px;
-      height: 50vh;
+      width: 50lvw;
+      height: 50lvh;
     }
 
     #spacer {
diff --git a/third_party/blink/renderer/core/testing/data/vh-height.html b/third_party/blink/renderer/core/testing/data/sv-size.html
similarity index 82%
copy from third_party/blink/renderer/core/testing/data/vh-height.html
copy to third_party/blink/renderer/core/testing/data/sv-size.html
index 98807ed..e38cfab 100644
--- a/third_party/blink/renderer/core/testing/data/vh-height.html
+++ b/third_party/blink/renderer/core/testing/data/sv-size.html
@@ -8,8 +8,8 @@
       left: 0px;
       top: 0px;
       background: silver;
-      width: 100px;
-      height: 50vh;
+      width: 50svw;
+      height: 50svh;
     }
 
     #fixed {
@@ -17,8 +17,8 @@
       right: 0px;
       top: 0px;
       background: red;
-      width: 100px;
-      height: 50vh;
+      width: 50svw;
+      height: 50svh;
     }
 
     #spacer {
diff --git a/third_party/blink/renderer/core/testing/data/vh-height.html b/third_party/blink/renderer/core/testing/data/v-size.html
similarity index 91%
rename from third_party/blink/renderer/core/testing/data/vh-height.html
rename to third_party/blink/renderer/core/testing/data/v-size.html
index 98807ed..bb0da21 100644
--- a/third_party/blink/renderer/core/testing/data/vh-height.html
+++ b/third_party/blink/renderer/core/testing/data/v-size.html
@@ -8,7 +8,7 @@
       left: 0px;
       top: 0px;
       background: silver;
-      width: 100px;
+      width: 50vw;
       height: 50vh;
     }
 
@@ -17,7 +17,7 @@
       right: 0px;
       top: 0px;
       background: red;
-      width: 100px;
+      width: 50vw;
       height: 50vh;
     }
 
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index 7408ee5c..91a6f8ad 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -882,99 +882,6 @@
           "one time."));
       return;
     }
-    case RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The provider's .well-known configuration cannot be found."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The response body is empty when fetching the provider's .well-known "
-          "configuration."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's .well-known configuration is invalid."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataHttpNotFound: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The provider's client metadata endpoint cannot be found."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataNoResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The response body is empty when fetching the provider's client "
-          "metadata."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataInvalidResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's client metadata is invalid."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingSignin: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "Error attempting to reach the provider's sign-in endpoint."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorInvalidSigninResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's sign-in response is invalid."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingAccountsHttpNotFound: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The provider's accounts list endpoint cannot be found."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingAccountsNoResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The response body is empty when fetching the provider's accounts "
-          "list."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's accounts list is invalid."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingIdTokenHttpNotFound: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The provider's id token endpoint cannot be found."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingIdTokenNoResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError,
-          "The response body is empty when fetching the provider's id token."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingIdTokenInvalidResponse: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's id token is invalid."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingIdTokenInvalidRequest: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "The id token fetching request is invalid."));
-      return;
-    }
     case RequestIdTokenStatus::kErrorCanceled: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kAbortError, "The request has been aborted."));
@@ -989,6 +896,9 @@
       resolver->Resolve(id_token);
       return;
     }
+    default: {
+      NOTREACHED();
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/modules/url_pattern/url_pattern_component.cc b/third_party/blink/renderer/modules/url_pattern/url_pattern_component.cc
index 66db96ee8..3ed5c21 100644
--- a/third_party/blink/renderer/modules/url_pattern/url_pattern_component.cc
+++ b/third_party/blink/renderer/modules/url_pattern/url_pattern_component.cc
@@ -364,7 +364,7 @@
   }
 
   // There is no regexp, so directly match against the pattern.
-  std::vector<std::pair<absl::string_view, absl::string_view>>
+  std::vector<std::pair<absl::string_view, absl::optional<absl::string_view>>>
       pattern_group_list;
   // Lossy UTF8 conversion is fine given the input has come through a
   // USVString webidl argument.
@@ -376,9 +376,22 @@
     group_list->ReserveInitialCapacity(
         base::checked_cast<wtf_size_t>(pattern_group_list.size()));
     for (const auto& pair : pattern_group_list) {
+      // We need to be careful converting the group value to a WTF::String.
+      // If the value is std::nullopt, then we want to use a null String.
+      // If the value exists, but is zero length, then we want to use an empty
+      // string.  We must handle this explicitly since FromUTF8() can convert
+      // some zero length strings to null String.
+      String value;
+      if (pair.second.has_value()) {
+        if (pair.second->empty()) {
+          value = g_empty_string;
+        } else {
+          value = String::FromUTF8(pair.second->data(), pair.second->length());
+        }
+      }
       group_list->emplace_back(
           String::FromUTF8(pair.first.data(), pair.first.length()),
-          String::FromUTF8(pair.second.data(), pair.second.length()));
+          std::move(value));
     }
   }
   return result;
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
index b6a44f9..083b95c 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
@@ -545,6 +545,9 @@
 
 template <>
 WGPUStoreOp AsDawnEnum<WGPUStoreOp>(const WTF::String& webgpu_enum) {
+  if (webgpu_enum.IsNull()) {
+    return WGPUStoreOp_Undefined;
+  }
   if (webgpu_enum == "store") {
     return WGPUStoreOp_Store;
   }
@@ -557,9 +560,15 @@
 
 template <>
 WGPULoadOp AsDawnEnum<WGPULoadOp>(const WTF::String& webgpu_enum) {
+  if (webgpu_enum.IsNull()) {
+    return WGPULoadOp_Undefined;
+  }
   if (webgpu_enum == "load") {
     return WGPULoadOp_Load;
   }
+  if (webgpu_enum == "clear") {
+    return WGPULoadOp_Clear;
+  }
   NOTREACHED();
   return WGPULoadOp_Force32;
 }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
index c65a124f..f034195 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_color_attachment.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_depth_stencil_attachment.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_descriptor.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_doublesequence_gpucolordict.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_doublesequence_gpucolordict_gpuloadop.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_float_gpuloadop.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_gpuloadop_unsignedlongenforcerange.h"
@@ -38,25 +39,39 @@
                                 ? webgpu_desc->resolveTarget()->GetHandle()
                                 : nullptr;
 
-  switch (webgpu_desc->loadValue()->GetContentType()) {
-    case V8UnionGPUColorOrGPULoadOp::ContentType::kGPULoadOp:
-      dawn_desc.loadOp =
-          AsDawnEnum<WGPULoadOp>(webgpu_desc->loadValue()->GetAsGPULoadOp());
-      break;
-    case V8UnionGPUColorOrGPULoadOp::ContentType::kGPUColorDict:
-      dawn_desc.loadOp = WGPULoadOp_Clear;
-      dawn_desc.clearColor =
-          AsDawnType(webgpu_desc->loadValue()->GetAsGPUColorDict());
-      break;
-    case V8UnionGPUColorOrGPULoadOp::ContentType::kDoubleSequence:
-      dawn_desc.loadOp = WGPULoadOp_Clear;
-      dawn_desc.clearColor =
-          AsDawnColor(webgpu_desc->loadValue()->GetAsDoubleSequence());
-      break;
+  if (webgpu_desc->hasClearValue()) {
+    dawn_desc.clearColor = AsDawnType(webgpu_desc->clearValue());
+  } else {
+    dawn_desc.clearColor = {};
+  }
+
+  if (webgpu_desc->hasLoadOp()) {
+    dawn_desc.loadOp = AsDawnEnum<WGPULoadOp>(webgpu_desc->loadOp());
+  } else if (webgpu_desc->hasLoadValue()) {
+    // TODO(dawn:1269): Remove this branch after the deprecation period.
+    switch (webgpu_desc->loadValue()->GetContentType()) {
+      case V8UnionGPUColorOrGPULoadOp::ContentType::kGPULoadOp:
+        dawn_desc.loadOp =
+            AsDawnEnum<WGPULoadOp>(webgpu_desc->loadValue()->GetAsGPULoadOp());
+        break;
+      case V8UnionGPUColorOrGPULoadOp::ContentType::kGPUColorDict:
+        dawn_desc.loadOp = WGPULoadOp_Clear;
+        dawn_desc.clearColor =
+            AsDawnType(webgpu_desc->loadValue()->GetAsGPUColorDict());
+        break;
+      case V8UnionGPUColorOrGPULoadOp::ContentType::kDoubleSequence:
+        dawn_desc.loadOp = WGPULoadOp_Clear;
+        dawn_desc.clearColor =
+            AsDawnColor(webgpu_desc->loadValue()->GetAsDoubleSequence());
+        break;
+    }
   }
 
   if (webgpu_desc->hasStoreOp()) {
     dawn_desc.storeOp = AsDawnEnum<WGPUStoreOp>(webgpu_desc->storeOp());
+  } else {
+    // TODO(dawn:1269): Remove when deprecation period is complete.
+    dawn_desc.storeOp = WGPUStoreOp_Store;
   }
 
   return dawn_desc;
@@ -65,42 +80,72 @@
 namespace {
 
 WGPURenderPassDepthStencilAttachment AsDawnType(
+    GPUDevice* device,
     const GPURenderPassDepthStencilAttachment* webgpu_desc) {
   DCHECK(webgpu_desc);
 
   WGPURenderPassDepthStencilAttachment dawn_desc = {};
     dawn_desc.view = webgpu_desc->view()->GetHandle();
 
-  switch (webgpu_desc->depthLoadValue()->GetContentType()) {
-    case V8UnionFloatOrGPULoadOp::ContentType::kGPULoadOp:
-      dawn_desc.depthLoadOp = AsDawnEnum<WGPULoadOp>(
-          webgpu_desc->depthLoadValue()->GetAsGPULoadOp());
-      dawn_desc.clearDepth = 1.0f;
-      break;
-    case V8UnionFloatOrGPULoadOp::ContentType::kFloat:
-      dawn_desc.depthLoadOp = WGPULoadOp_Clear;
-      dawn_desc.clearDepth = webgpu_desc->depthLoadValue()->GetAsFloat();
-      break;
-  }
+    if (webgpu_desc->hasDepthLoadOp()) {
+      dawn_desc.depthLoadOp =
+          AsDawnEnum<WGPULoadOp>(webgpu_desc->depthLoadOp());
+      dawn_desc.clearDepth = webgpu_desc->depthClearValue();
+    } else if (webgpu_desc->hasDepthLoadValue()) {
+      // TODO(dawn:1269): Remove this branch after the deprecation period.
+      device->AddConsoleWarning(
+          "depthLoadValue has been deprecated and will soon be removed. Use "
+          "depthLoadOp and depthClearValue instead.");
 
-  dawn_desc.depthStoreOp = AsDawnEnum<WGPUStoreOp>(webgpu_desc->depthStoreOp());
-  dawn_desc.depthReadOnly = webgpu_desc->depthReadOnly();
+      switch (webgpu_desc->depthLoadValue()->GetContentType()) {
+        case V8UnionFloatOrGPULoadOp::ContentType::kGPULoadOp:
+          dawn_desc.depthLoadOp = AsDawnEnum<WGPULoadOp>(
+              webgpu_desc->depthLoadValue()->GetAsGPULoadOp());
+          dawn_desc.clearDepth = 1.0f;
+          break;
+        case V8UnionFloatOrGPULoadOp::ContentType::kFloat:
+          dawn_desc.depthLoadOp = WGPULoadOp_Clear;
+          dawn_desc.clearDepth = webgpu_desc->depthLoadValue()->GetAsFloat();
+          break;
+      }
+    }
 
-  switch (webgpu_desc->stencilLoadValue()->GetContentType()) {
-    case V8UnionGPULoadOpOrGPUStencilValue::ContentType::kGPULoadOp:
-      dawn_desc.stencilLoadOp = AsDawnEnum<WGPULoadOp>(
-          webgpu_desc->stencilLoadValue()->GetAsGPULoadOp());
-      dawn_desc.clearStencil = 0;
-      break;
-    case V8UnionGPULoadOpOrGPUStencilValue::ContentType::kV8GPUStencilValue:
-      dawn_desc.stencilLoadOp = WGPULoadOp_Clear;
-      dawn_desc.clearStencil =
-          webgpu_desc->stencilLoadValue()->GetAsV8GPUStencilValue();
-      break;
-  }
+    if (webgpu_desc->hasDepthStoreOp()) {
+      dawn_desc.depthStoreOp =
+          AsDawnEnum<WGPUStoreOp>(webgpu_desc->depthStoreOp());
+    }
 
-  dawn_desc.stencilStoreOp =
-      AsDawnEnum<WGPUStoreOp>(webgpu_desc->stencilStoreOp());
+    dawn_desc.depthReadOnly = webgpu_desc->depthReadOnly();
+
+    if (webgpu_desc->hasStencilLoadOp()) {
+      dawn_desc.stencilLoadOp =
+          AsDawnEnum<WGPULoadOp>(webgpu_desc->stencilLoadOp());
+      dawn_desc.clearStencil = webgpu_desc->stencilClearValue();
+    } else if (webgpu_desc->hasStencilLoadValue()) {
+      // TODO(dawn:1269): Remove this branch after the deprecation period.
+      device->AddConsoleWarning(
+          "stencilLoadValue has been deprecated and will soon be removed. Use "
+          "stencilLoadOp and stencilClearValue instead.");
+
+      switch (webgpu_desc->stencilLoadValue()->GetContentType()) {
+        case V8UnionGPULoadOpOrGPUStencilValue::ContentType::kGPULoadOp:
+          dawn_desc.stencilLoadOp = AsDawnEnum<WGPULoadOp>(
+              webgpu_desc->stencilLoadValue()->GetAsGPULoadOp());
+          dawn_desc.clearStencil = 0;
+          break;
+        case V8UnionGPULoadOpOrGPUStencilValue::ContentType::kV8GPUStencilValue:
+          dawn_desc.stencilLoadOp = WGPULoadOp_Clear;
+          dawn_desc.clearStencil =
+              webgpu_desc->stencilLoadValue()->GetAsV8GPUStencilValue();
+          break;
+      }
+    }
+
+    if (webgpu_desc->hasStencilStoreOp()) {
+      dawn_desc.stencilStoreOp =
+          AsDawnEnum<WGPUStoreOp>(webgpu_desc->stencilStoreOp());
+    }
+
   dawn_desc.stencilReadOnly = webgpu_desc->stencilReadOnly();
 
   return dawn_desc;
@@ -176,10 +221,28 @@
     const GPURenderPassColorAttachment* color_attachment =
         descriptor->colorAttachments()[i];
 
-    if (color_attachment->loadValue()->IsDoubleSequence() &&
-        color_attachment->loadValue()->GetAsDoubleSequence().size() != 4) {
-      exception_state.ThrowRangeError("loadValue color size must be 4");
-      return nullptr;
+    if (color_attachment->hasLoadOp()) {
+      if (color_attachment->hasClearValue() &&
+          color_attachment->clearValue()->IsDoubleSequence() &&
+          color_attachment->clearValue()->GetAsDoubleSequence().size() != 4) {
+        exception_state.ThrowRangeError("clearValue color size must be 4");
+        return nullptr;
+      }
+    } else if (color_attachment->hasLoadValue()) {
+      if (color_attachment->loadValue()->IsDoubleSequence() &&
+          color_attachment->loadValue()->GetAsDoubleSequence().size() != 4) {
+        exception_state.ThrowRangeError("loadValue color size must be 4");
+        return nullptr;
+      }
+
+      device_->AddConsoleWarning(
+          "loadValue has been deprecated and will soon be removed. Use loadOp "
+          "and clearValue instead.");
+    }
+
+    if (!color_attachment->hasStoreOp()) {
+      device_->AddConsoleWarning(
+          "storeOp will soon be required and no longer default to 'store'.");
     }
   }
 
@@ -201,7 +264,9 @@
 
   WGPURenderPassDepthStencilAttachment depthStencilAttachment = {};
   if (descriptor->hasDepthStencilAttachment()) {
-    depthStencilAttachment = AsDawnType(descriptor->depthStencilAttachment());
+    const GPURenderPassDepthStencilAttachment* depth_stencil =
+        descriptor->depthStencilAttachment();
+    depthStencilAttachment = AsDawnType(device_, depth_stencil);
     dawn_desc.depthStencilAttachment = &depthStencilAttachment;
   } else {
     dawn_desc.depthStencilAttachment = nullptr;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment.idl
index 842872af..7e23724d5 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment.idl
@@ -8,6 +8,13 @@
     required GPUTextureView view;
     GPUTextureView resolveTarget;
 
-    required (GPULoadOp or GPUColor) loadValue;
-    GPUStoreOp storeOp = "store";
+    GPUColor clearValue;
+
+    // TODO(dawn:1269): loadOp and storeOp should be required after deprecation
+    // period.
+    GPULoadOp loadOp;
+    GPUStoreOp storeOp;
+
+    // TODO(dawn:1269): Deprecated, prefer loadOp+clearValue instead.
+    (GPULoadOp or GPUColor) loadValue;
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment.idl
index a6faa51..34d3198 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment.idl
@@ -7,11 +7,17 @@
 dictionary GPURenderPassDepthStencilAttachment {
     required GPUTextureView view;
 
-    required (GPULoadOp or float) depthLoadValue;
-    required GPUStoreOp depthStoreOp;
+    float depthClearValue = 0;
+    GPULoadOp depthLoadOp;
+    GPUStoreOp depthStoreOp;
     boolean depthReadOnly = false;
 
-    required (GPULoadOp or GPUStencilValue) stencilLoadValue;
-    required GPUStoreOp stencilStoreOp;
+    GPUStencilValue stencilClearValue = 0;
+    GPULoadOp stencilLoadOp;
+    GPUStoreOp stencilStoreOp;
     boolean stencilReadOnly = false;
+
+    // TODO(dawn:1269): Deprecated, prefer *LoadOp+*ClearValue instead.
+    (GPULoadOp or float) depthLoadValue;
+    (GPULoadOp or GPUStencilValue) stencilLoadValue;
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.idl b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.idl
index ab42f710..901eb4b 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.idl
@@ -11,7 +11,8 @@
 };
 
 enum GPULoadOp {
-    "load"
+    "load",
+    "clear",
 };
 
 enum GPUStoreOp {
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index ae3947d..214a21b 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -8,6 +8,7 @@
 import("//testing/test.gni")
 import("//third_party/blink/public/public_features.gni")
 import("//third_party/blink/renderer/platform/platform.gni")
+import("//v8/gni/v8.gni")
 
 buildflag_header("blink_heap_buildflags") {
   header = "heap_buildflags.h"
@@ -15,7 +16,7 @@
 
   flags = [
     "BLINK_HEAP_VERIFICATION=$enable_blink_heap_verification",
-    "BLINK_HEAP_YOUNG_GENERATION=$enable_blink_heap_young_generation",
+    "BLINK_HEAP_YOUNG_GENERATION=$cppgc_enable_young_generation",
   ]
 }
 
@@ -148,7 +149,7 @@
     "test/weakness_marking_test.cc",
   ]
 
-  if (enable_blink_heap_young_generation) {
+  if (cppgc_enable_young_generation) {
     sources += [ "test/minor_gc_test.cc" ]
   }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator_impl.h b/third_party/blink/renderer/platform/heap/heap_allocator_impl.h
index 5e9f9a0..841292f 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator_impl.h
+++ b/third_party/blink/renderer/platform/heap/heap_allocator_impl.h
@@ -150,6 +150,33 @@
     }
   }
 
+  template <typename T>
+  static void GenerationalBarrierForBacking(
+      const HeapConsistency::WriteBarrierParams& params,
+      T* slot_in_backing) {
+    if constexpr (WTF::IsMemberOrWeakMemberType<std::decay_t<T>>::value) {
+      // TODO(1029379): Provide GetSlot() and call it here.
+      HeapConsistency::GenerationalBarrier(params, slot_in_backing);
+    }
+    // TODO(1029379): Handle other traceable types.
+  }
+
+  template <typename K, typename V>
+  static void GenerationalBarrierForBacking(
+      const HeapConsistency::WriteBarrierParams& params,
+      std::pair<K, V>* slot_in_backing) {
+    GenerationalBarrierForBacking(params, &slot_in_backing->first);
+    GenerationalBarrierForBacking(params, &slot_in_backing->second);
+  }
+
+  template <typename K, typename V>
+  static void GenerationalBarrierForBacking(
+      const HeapConsistency::WriteBarrierParams& params,
+      WTF::KeyValuePair<K, V>* slot_in_backing) {
+    GenerationalBarrierForBacking(params, &slot_in_backing->key);
+    GenerationalBarrierForBacking(params, &slot_in_backing->value);
+  }
+
   template <typename T, typename Traits>
   static void NotifyNewObject(T* slot_in_backing) {
     HeapConsistency::WriteBarrierParams params;
@@ -166,7 +193,7 @@
             TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace);
         break;
       case HeapConsistency::WriteBarrierType::kGenerational:
-        HeapConsistency::GenerationalBarrier(params, slot_in_backing);
+        GenerationalBarrierForBacking(params, slot_in_backing);
         break;
       case HeapConsistency::WriteBarrierType::kNone:
         break;
@@ -191,7 +218,7 @@
             TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace);
         break;
       case HeapConsistency::WriteBarrierType::kGenerational:
-        HeapConsistency::GenerationalBarrier(params, first_element);
+        GenerationalBarrierForBacking(params, first_element);
         break;
       case HeapConsistency::WriteBarrierType::kNone:
         break;
diff --git a/third_party/blink/renderer/platform/heap/test/minor_gc_test.cc b/third_party/blink/renderer/platform/heap/test/minor_gc_test.cc
index 7ba9eaf..f07ba95 100644
--- a/third_party/blink/renderer/platform/heap/test/minor_gc_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/minor_gc_test.cc
@@ -3,16 +3,25 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/heap/heap_page.h"
+
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/cppgc/testing.h"
 
 namespace blink {
 
 namespace {
 
+bool IsOld(void* object) {
+  return cppgc::testing::IsHeapObjectOld(object);
+}
+
 class SimpleGCedBase : public GarbageCollected<SimpleGCedBase> {
  public:
   static size_t destructed_objects;
@@ -56,14 +65,14 @@
     return SimpleGCedBase::destructed_objects;
   }
 
-  static void CollectMinor() { Collect(BlinkGC::CollectionType::kMinor); }
-  static void CollectMajor() { Collect(BlinkGC::CollectionType::kMajor); }
+  static void CollectMinor() {
+    ThreadState::Current()->CollectGarbageInYoungGenerationForTesting(
+        ThreadState::StackState::kNoHeapPointers);
+  }
 
- private:
-  static void Collect(BlinkGC::CollectionType type) {
-    ThreadState::Current()->CollectGarbage(
-        type, BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting);
+  static void CollectMajor() {
+    ThreadState::Current()->CollectAllGarbageForTesting(
+        ThreadState::StackState::kNoHeapPointers);
   }
 };
 
@@ -78,98 +87,6 @@
 using ObjectTypes = ::testing::Types<Small, Large>;
 TYPED_TEST_SUITE(MinorGCTestForType, ObjectTypes);
 
-TYPED_TEST(MinorGCTestForType, MinorCollection) {
-  using Type = typename TestFixture::Type;
-
-  MakeGarbageCollected<Type>();
-  EXPECT_EQ(0u, TestFixture::DestructedObjects());
-  MinorGCTest::CollectMinor();
-  EXPECT_EQ(1u, TestFixture::DestructedObjects());
-
-  Type* prev = nullptr;
-  for (size_t i = 0; i < 64; ++i) {
-    auto* ptr = MakeGarbageCollected<Type>();
-    ptr->next = prev;
-    prev = ptr;
-  }
-
-  MinorGCTest::CollectMinor();
-  EXPECT_EQ(65u, TestFixture::DestructedObjects());
-}
-
-TYPED_TEST(MinorGCTestForType, StickyBits) {
-  using Type = typename TestFixture::Type;
-
-  Persistent<Type> p1 = MakeGarbageCollected<Type>();
-  TestFixture::CollectMinor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(p1.Get())->IsOld());
-  TestFixture::CollectMajor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(p1.Get())->IsOld());
-  EXPECT_EQ(0u, TestFixture::DestructedObjects());
-}
-
-TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) {
-  using Type = typename TestFixture::Type;
-
-  Persistent<Type> p = MakeGarbageCollected<Type>();
-  TestFixture::CollectMinor();
-  EXPECT_EQ(0u, TestFixture::DestructedObjects());
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(p.Get())->IsOld());
-
-  // Check that the old deleted object won't be visited during minor GC.
-  Type* raw = p.Release();
-  TestFixture::CollectMinor();
-  EXPECT_EQ(0u, TestFixture::DestructedObjects());
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(raw)->IsOld());
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(raw)->IsFree());
-
-  // Check that the old deleted object will be revisited in major GC.
-  TestFixture::CollectMajor();
-  EXPECT_EQ(1u, TestFixture::DestructedObjects());
-}
-
-template <typename Type1, typename Type2>
-void InterGenerationalPointerTest() {
-  Persistent<Type1> old = MakeGarbageCollected<Type1>();
-  MinorGCTest::CollectMinor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(old.Get())->IsOld());
-
-  // Allocate young objects.
-  Type2* young = nullptr;
-  for (size_t i = 0; i < 64; ++i) {
-    auto* ptr = MakeGarbageCollected<Type2>();
-    ptr->next = young;
-    young = ptr;
-    EXPECT_FALSE(HeapObjectHeader::FromPayload(young)->IsOld());
-  }
-
-  // Issue generational barrier.
-  old->next = young;
-
-  // Check that the remembered set is visited.
-  MinorGCTest::CollectMinor();
-  EXPECT_EQ(0u, MinorGCTest::DestructedObjects());
-  for (size_t i = 0; i < 64; ++i) {
-    EXPECT_TRUE(HeapObjectHeader::FromPayload(young)->IsOld());
-    EXPECT_FALSE(HeapObjectHeader::FromPayload(young)->IsFree());
-    young = static_cast<Type2*>(young->next.Get());
-  }
-
-  old.Release();
-  MinorGCTest::CollectMajor();
-  EXPECT_EQ(65u, MinorGCTest::DestructedObjects());
-}
-
-TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForSamePageTypes) {
-  using Type = typename TestFixture::Type;
-  InterGenerationalPointerTest<Type, Type>();
-}
-
-TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForDifferentPageTypes) {
-  using Type = typename TestFixture::Type;
-  InterGenerationalPointerTest<Type, typename OtherType<Type>::Type>();
-}
-
 TYPED_TEST(MinorGCTestForType, InterGenerationalPointerInCollection) {
   using Type = typename TestFixture::Type;
 
@@ -178,9 +95,9 @@
       MakeGarbageCollected<HeapVector<Member<Type>>>();
   old->resize(kCollectionSize);
   void* raw_backing = old->data();
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_FALSE(IsOld(raw_backing));
   MinorGCTest::CollectMinor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_TRUE(IsOld(raw_backing));
 
   // Issue barrier for every second member.
   size_t i = 0;
@@ -198,8 +115,7 @@
   EXPECT_EQ(kCollectionSize / 2, MinorGCTest::DestructedObjects());
   for (const auto& member : *old) {
     if (member) {
-      EXPECT_TRUE(HeapObjectHeader::FromPayload(member.Get())->IsOld());
-      EXPECT_FALSE(HeapObjectHeader::FromPayload(member.Get())->IsFree());
+      EXPECT_TRUE(IsOld(member.Get()));
     }
   }
 
@@ -219,13 +135,16 @@
   old->ReserveInitialCapacity(kCollectionSize);
 
   void* raw_backing = old->data();
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_FALSE(IsOld(raw_backing));
   MinorGCTest::CollectMinor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_TRUE(IsOld(raw_backing));
 
   // Issue barrier (in HeapAllocator::NotifyNewElement).
   old->push_back(std::make_pair("test", MakeGarbageCollected<Type>()));
 
+  // Store the reference in a weak pointer to check liveness.
+  WeakPersistent<Type> object_is_live = (*old)[0].second;
+
   // Check that the remembered set is visited.
   MinorGCTest::CollectMinor();
 
@@ -235,12 +154,13 @@
 
   {
     Type* member = (*old)[0].second;
-    EXPECT_TRUE(HeapObjectHeader::FromPayload(member)->IsOld());
-    EXPECT_FALSE(HeapObjectHeader::FromPayload(member)->IsFree());
+    EXPECT_TRUE(IsOld(member));
+    EXPECT_TRUE(object_is_live);
   }
 
   old.Release();
   MinorGCTest::CollectMajor();
+  EXPECT_FALSE(object_is_live);
   EXPECT_EQ(1u, MinorGCTest::DestructedObjects());
 }
 
@@ -257,17 +177,20 @@
   old->ReserveInitialCapacity(1);
 
   void* raw_backing = old->data();
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_FALSE(IsOld(raw_backing));
 
   // Mark old backing.
   MinorGCTest::CollectMinor();
-  EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld());
+  EXPECT_TRUE(IsOld(raw_backing));
 
   Persistent<CollectionType> young = MakeGarbageCollected<CollectionType>();
 
   // Add a single element to the young container.
   young->push_back(std::make_pair(1, MakeGarbageCollected<Type>()));
 
+  // Store the reference in a weak pointer to check liveness.
+  WeakPersistent<Type> object_is_live = (*young)[0].second;
+
   // Copy young container and issue barrier in HeapAllocator::NotifyNewElements.
   *old = *young;
 
@@ -283,12 +206,13 @@
 
   {
     Type* member = (*old)[0].second;
-    EXPECT_TRUE(HeapObjectHeader::FromPayload(member)->IsOld());
-    EXPECT_FALSE(HeapObjectHeader::FromPayload(member)->IsFree());
+    EXPECT_TRUE(IsOld(member));
+    EXPECT_TRUE(object_is_live);
   }
 
   old.Release();
   MinorGCTest::CollectMajor();
+  EXPECT_FALSE(object_is_live);
   EXPECT_EQ(1u, MinorGCTest::DestructedObjects());
 }
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 1976729..ace4df55 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -208,6 +208,11 @@
   }
 }
 
+void ThreadState::CollectGarbageInYoungGenerationForTesting(
+    StackState stack_state) {
+  cpp_heap().CollectGarbageInYoungGenerationForTesting(stack_state);
+}
+
 namespace {
 
 class CustomSpaceStatisticsReceiverImpl final
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 8bcc969..9e0671d 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -92,6 +92,10 @@
   void CollectAllGarbageForTesting(
       StackState stack_state = StackState::kNoHeapPointers);
 
+  // Perform stop-the-world garbage collection in young generation for testing.
+  void CollectGarbageInYoungGenerationForTesting(
+      StackState stack_state = StackState::kNoHeapPointers);
+
   void EnableDetachedGarbageCollectionsForTesting();
 
   static ThreadState* AttachMainThreadForTesting(v8::Platform*);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index bf607450..cc319f6d7 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -824,6 +824,13 @@
       status: "experimental"
     },
     {
+      // Support for viewport units added by css-values-4.
+      //
+      // https://drafts.csswg.org/css-values-4/#viewport-relative-units
+      name: "CSSViewportUnits4",
+      status: "experimental",
+    },
+    {
       name: "CustomElementDefaultStyle",
       status: "experimental",
     },
@@ -1758,7 +1765,6 @@
     // TODO(crbug.com/1209835): this feature is getting deprecated.
     {
       name: "PaymentRequestBasicCard",
-      status: "stable",
     },
     {
       name: "PaymentRequestMerchantValidationEvent",
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 2db34d3..e830415 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -165,6 +165,9 @@
                                const SecurityOrigin* precursor)
     : nonce_if_opaque_(nonce), precursor_origin_(precursor) {}
 
+SecurityOrigin::SecurityOrigin(NewUniqueOpaque, const SecurityOrigin* precursor)
+    : nonce_if_opaque_(absl::in_place), precursor_origin_(precursor) {}
+
 SecurityOrigin::SecurityOrigin(const SecurityOrigin* other,
                                ConstructIsolatedCopy)
     : protocol_(other->protocol_.IsolatedCopy()),
@@ -235,8 +238,8 @@
 }
 
 scoped_refptr<SecurityOrigin> SecurityOrigin::CreateUniqueOpaque() {
-  scoped_refptr<SecurityOrigin> origin =
-      base::AdoptRef(new SecurityOrigin(url::Origin::Nonce(), nullptr));
+  scoped_refptr<SecurityOrigin> origin = base::AdoptRef(
+      new SecurityOrigin(NewUniqueOpaque::kWithLazyInitNonce, nullptr));
   DCHECK(origin->IsOpaque());
   DCHECK(!origin->precursor_origin_);
   return origin;
@@ -676,8 +679,9 @@
 }
 
 scoped_refptr<SecurityOrigin> SecurityOrigin::DeriveNewOpaqueOrigin() const {
-  return base::AdoptRef(new SecurityOrigin(
-      url::Origin::Nonce(), GetOriginOrPrecursorOriginIfOpaque()));
+  return base::AdoptRef(
+      new SecurityOrigin(NewUniqueOpaque::kWithLazyInitNonce,
+                         GetOriginOrPrecursorOriginIfOpaque()));
 }
 
 const SecurityOrigin* SecurityOrigin::GetOriginOrPrecursorOriginIfOpaque()
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.h b/third_party/blink/renderer/platform/weborigin/security_origin.h
index 72de1b6..def3999 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.h
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.h
@@ -48,6 +48,7 @@
 
 class KURL;
 struct SecurityOriginHash;
+class SecurityOriginTest;
 
 // An identifier which defines the source of content (e.g. a document) and
 // restricts what other objects it is permitted to access (based on their
@@ -161,7 +162,14 @@
     AccessResultDomainDetail unused_detail;
     return CanAccess(other, unused_detail);
   }
+  bool CanAccess(const scoped_refptr<SecurityOrigin>& other) const {
+    return CanAccess(other.get());
+  }
   bool CanAccess(const SecurityOrigin* other, AccessResultDomainDetail&) const;
+  bool CanAccess(const scoped_refptr<SecurityOrigin>& other,
+                 AccessResultDomainDetail& detail) const {
+    return CanAccess(other.get(), detail);
+  }
 
   // Returns true if this SecurityOrigin can read content retrieved from
   // the given URL.
@@ -379,8 +387,10 @@
   bool SerializesAsNull() const;
 
  private:
-  friend struct mojo::UrlOriginAdapter;
-  friend struct blink::SecurityOriginHash;
+  // Various serialisation and test routines that need direct nonce access.
+  friend mojo::UrlOriginAdapter;
+  friend SecurityOriginHash;
+  friend SecurityOriginTest;
 
   // For calling GetNonceForSerialization().
   friend class BlobURLOpaqueOriginNonceMap;
@@ -395,6 +405,14 @@
   SecurityOrigin(const url::Origin::Nonce& nonce,
                  const SecurityOrigin* precursor_origin);
 
+  // Creates an opaque SecurityOrigin with a new unique nonce. Similar to the
+  // above, but preferred when there is no pre-existing nonce to copy, as
+  // copying a nonce requires forcing eager initialisation of that nonce.
+  enum class NewUniqueOpaque {
+    kWithLazyInitNonce,
+  };
+  SecurityOrigin(NewUniqueOpaque, const SecurityOrigin* precursor_origin);
+
   // Create a tuple SecurityOrigin, with parameters via KURL
   explicit SecurityOrigin(const KURL& url);
 
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index 587ed2a..6fb911d0 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -34,6 +34,7 @@
 
 #include "base/cxx17_backports.h"
 #include "base/test/scoped_command_line.h"
+#include "base/unguessable_token.h"
 #include "net/base/url_util.h"
 #include "services/network/public/cpp/is_potentially_trustworthy_unittest.h"
 #include "services/network/public/cpp/network_switches.h"
@@ -58,8 +59,18 @@
 const uint16_t kMaxAllowedPort = UINT16_MAX;
 
 class SecurityOriginTest : public testing::Test {
- private:
+ protected:
   void TearDown() override { SecurityPolicy::ClearOriginAccessList(); }
+
+  const absl::optional<url::Origin::Nonce>& GetNonceForOrigin(
+      const SecurityOrigin& origin) {
+    return origin.nonce_if_opaque_;
+  }
+
+  const absl::optional<base::UnguessableToken>
+  GetNonceForSerializationForOrigin(const SecurityOrigin& origin) {
+    return origin.GetNonceForSerialization();
+  }
 };
 
 TEST_F(SecurityOriginTest, ValidPortsCreateTupleOrigins) {
@@ -1096,6 +1107,47 @@
       "foo%2C.example.test");
 }
 
+TEST_F(SecurityOriginTest, NewOpaqueOriginLazyInitsNonce) {
+  scoped_refptr<SecurityOrigin> opaque_origin =
+      SecurityOrigin::CreateUniqueOpaque();
+
+  scoped_refptr<SecurityOrigin> tuple_origin =
+      SecurityOrigin::Create(KURL("https://example.com/"));
+  scoped_refptr<SecurityOrigin> derived_opaque_origin =
+      tuple_origin->DeriveNewOpaqueOrigin();
+
+  EXPECT_TRUE(opaque_origin->IsOpaque());
+  // There should be a nonce...
+  EXPECT_TRUE(GetNonceForOrigin(*opaque_origin).has_value());
+  // ...but it should not be initialised yet.
+  EXPECT_TRUE(GetNonceForOrigin(*opaque_origin)->raw_token().is_empty());
+
+  EXPECT_TRUE(derived_opaque_origin->IsOpaque());
+  // There should be a nonce...
+  EXPECT_TRUE(GetNonceForOrigin(*derived_opaque_origin).has_value());
+  // ...but it should not be initialised yet.
+  EXPECT_TRUE(
+      GetNonceForOrigin(*derived_opaque_origin)->raw_token().is_empty());
+
+  // Even checking CanAccess does not need to trigger initialisation: two
+  // uninitialised nonces can only be equal if they are the same object.
+  EXPECT_TRUE(opaque_origin->CanAccess(opaque_origin));
+  EXPECT_FALSE(opaque_origin->CanAccess(derived_opaque_origin));
+  EXPECT_TRUE(derived_opaque_origin->CanAccess(derived_opaque_origin));
+
+  EXPECT_TRUE(GetNonceForOrigin(*opaque_origin)->raw_token().is_empty());
+  EXPECT_TRUE(
+      GetNonceForOrigin(*derived_opaque_origin)->raw_token().is_empty());
+
+  // However, forcing the nonce to be serialized should trigger initialisation.
+  (void)GetNonceForSerializationForOrigin(*opaque_origin);
+  (void)GetNonceForSerializationForOrigin(*derived_opaque_origin);
+
+  EXPECT_FALSE(GetNonceForOrigin(*opaque_origin)->raw_token().is_empty());
+  EXPECT_FALSE(
+      GetNonceForOrigin(*derived_opaque_origin)->raw_token().is_empty());
+}
+
 }  // namespace blink
 
 // Apparently INSTANTIATE_TYPED_TEST_SUITE_P needs to be used in the same
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index b951a67c..86bf01c 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -620,9 +620,9 @@
     "args": ["--disable-blink-features=PaymentRequestTotalOptional"]
   },
   {
-    "prefix": "basic-card-disabled",
+    "prefix": "basic-card-enabled",
     "bases": ["payments/payment-request-interface.html", "http/tests/payments/payment-instruments.html"],
-    "args": ["--disable-blink-features=PaymentRequestBasicCard"]
+    "args": ["--enable-blink-features=PaymentRequestBasicCard", "--enable-features=PaymentRequestBasicCard"]
   },
   {
     "prefix": "json-modules",
@@ -802,7 +802,8 @@
       "external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https.html",
       "external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https.html",
       "external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https.html",
-      "external/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html"
+      "external/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html",
+      "external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html"
     ],
     "args": [ "--enable-features=ThirdPartyStoragePartitioning" ]
   },
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/idlharness-expected.txt
index 3470bea6..59c039ac 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/idlharness-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 508 tests; 440 PASS, 68 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 508 tests; 442 PASS, 66 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Partial interface Element: original interface defined
@@ -481,8 +481,8 @@
 FAIL CSS namespace: operation rlh(double) assert_own_property: namespace object missing operation "rlh" expected property "rlh" missing
 PASS CSS namespace: operation vw(double)
 PASS CSS namespace: operation vh(double)
-FAIL CSS namespace: operation vi(double) assert_own_property: namespace object missing operation "vi" expected property "vi" missing
-FAIL CSS namespace: operation vb(double) assert_own_property: namespace object missing operation "vb" expected property "vb" missing
+PASS CSS namespace: operation vi(double)
+PASS CSS namespace: operation vb(double)
 PASS CSS namespace: operation vmin(double)
 PASS CSS namespace: operation vmax(double)
 PASS CSS namespace: operation cm(double)
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssUnitValue-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssUnitValue-expected.txt
index 36ed6fb3..79f57d4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssUnitValue-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssUnitValue-expected.txt
@@ -12,8 +12,8 @@
 FAIL CSSUnitValue can be constructed with rlh Failed to construct 'CSSUnitValue': Invalid unit: rlh
 PASS CSSUnitValue can be constructed with vw
 PASS CSSUnitValue can be constructed with vh
-FAIL CSSUnitValue can be constructed with vi Failed to construct 'CSSUnitValue': Invalid unit: vi
-FAIL CSSUnitValue can be constructed with vb Failed to construct 'CSSUnitValue': Invalid unit: vb
+PASS CSSUnitValue can be constructed with vi
+PASS CSSUnitValue can be constructed with vb
 PASS CSSUnitValue can be constructed with vmin
 PASS CSSUnitValue can be constructed with vmax
 PASS CSSUnitValue can be constructed with cm
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/numeric-factory.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/numeric-factory.tentative-expected.txt
index c768f12..6de3224 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/numeric-factory.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/numeric-factory.tentative-expected.txt
@@ -10,8 +10,8 @@
 FAIL CSS.rlh returns a CSSUnitValue with correct value and unit CSS[unit] is not a function
 PASS CSS.vw returns a CSSUnitValue with correct value and unit
 PASS CSS.vh returns a CSSUnitValue with correct value and unit
-FAIL CSS.vi returns a CSSUnitValue with correct value and unit CSS[unit] is not a function
-FAIL CSS.vb returns a CSSUnitValue with correct value and unit CSS[unit] is not a function
+PASS CSS.vi returns a CSSUnitValue with correct value and unit
+PASS CSS.vb returns a CSSUnitValue with correct value and unit
 PASS CSS.vmin returns a CSSUnitValue with correct value and unit
 PASS CSS.vmax returns a CSSUnitValue with correct value and unit
 PASS CSS.cm returns a CSSUnitValue with correct value and unit
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-compute.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-compute.html
new file mode 100644
index 0000000..7310946c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-compute.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>Resolving viewport units</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+  iframe {
+    width: 200px;
+    height: 100px;
+  }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_computed_value(value, expected) {
+  test((t) => {
+    t.add_cleanup(() => { doc.body.innerHTML = ''; });
+    doc.body.innerHTML = `
+      <style>
+        * { margin: 0; }
+        div { height: ${value}; }
+      </style>
+      <div></div>
+    `;
+    let div = doc.querySelector('div');
+    assert_equals(win.getComputedStyle(div).height, expected);
+  }, `${value} computes to ${expected}`);
+}
+
+test_computed_value('100vw', '200px');
+test_computed_value('100vi', '200px');
+test_computed_value('100vmax', '200px');
+test_computed_value('100svw', '200px');
+test_computed_value('100svi', '200px');
+test_computed_value('100svmax', '200px');
+test_computed_value('100lvw', '200px');
+test_computed_value('100lvi', '200px');
+test_computed_value('100lvmax', '200px');
+test_computed_value('100dvw', '200px');
+test_computed_value('100dvi', '200px');
+test_computed_value('100dvmax', '200px');
+
+test_computed_value('100vh', '100px');
+test_computed_value('100vb', '100px');
+test_computed_value('100vmin', '100px');
+test_computed_value('100svh', '100px');
+test_computed_value('100svb', '100px');
+test_computed_value('100svmin', '100px');
+test_computed_value('100lvh', '100px');
+test_computed_value('100lvb', '100px');
+test_computed_value('100lvmin', '100px');
+test_computed_value('100dvh', '100px');
+test_computed_value('100dvb', '100px');
+test_computed_value('100dvmin', '100px');
+
+test_computed_value('1dvw', '2px');
+test_computed_value('10dvw', '20px');
+test_computed_value('1dvh', '1px');
+test_computed_value('10dvh', '10px');
+
+test_computed_value('calc(1dvw + 1dvw)', '4px');
+test_computed_value('calc(1dvw + 1dvh)', '3px');
+test_computed_value('calc(1dvw + 100px)', '102px');
+test_computed_value('max(1svw, 1svh)', '2px');
+test_computed_value('min(1lvw, 1lvh)', '1px');
+test_computed_value('calc(1dvw + 10%)', '12px');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-invalidation.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-invalidation.html
new file mode 100644
index 0000000..93512ab9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-invalidation.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<title>Invalidation of viewport units</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+  iframe {
+    width: 200px;
+    height: 100px;
+  }
+
+  iframe.resize {
+    width: 400px;
+    height: 300px;
+  }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_invalidation_value(value, expected_pre, expected_post) {
+  test((t) => {
+    t.add_cleanup(() => { doc.body.innerHTML = ''; });
+    doc.body.innerHTML = `<div style="height: ${value};"></div>`;
+    let div = doc.querySelector('div');
+    assert_equals(win.getComputedStyle(div).height, expected_pre);
+
+    t.add_cleanup(() => { iframe.classList.remove('resize'); })
+    iframe.classList.add('resize');
+    assert_equals(win.getComputedStyle(div).height, expected_post);
+  }, `${value} computes to ${expected_post} after frame resize`);
+}
+
+test_invalidation_value('100vw', '200px', '400px');
+test_invalidation_value('100vi', '200px', '400px');
+test_invalidation_value('100vmax', '200px', '400px');
+test_invalidation_value('100svw', '200px', '400px');
+test_invalidation_value('100svi', '200px', '400px');
+test_invalidation_value('100svmax', '200px', '400px');
+test_invalidation_value('100lvw', '200px', '400px');
+test_invalidation_value('100lvi', '200px', '400px');
+test_invalidation_value('100lvmax', '200px', '400px');
+test_invalidation_value('100dvw', '200px', '400px');
+test_invalidation_value('100dvi', '200px', '400px');
+test_invalidation_value('100dvmax', '200px', '400px');
+
+test_invalidation_value('100vh', '100px', '300px');
+test_invalidation_value('100vb', '100px', '300px');
+test_invalidation_value('100vmin', '100px', '300px');
+test_invalidation_value('100svh', '100px', '300px');
+test_invalidation_value('100svb', '100px', '300px');
+test_invalidation_value('100svmin', '100px', '300px');
+test_invalidation_value('100lvh', '100px', '300px');
+test_invalidation_value('100lvb', '100px', '300px');
+test_invalidation_value('100lvmin', '100px', '300px');
+test_invalidation_value('100dvh', '100px', '300px');
+test_invalidation_value('100dvb', '100px', '300px');
+test_invalidation_value('100dvmin', '100px', '300px');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-keyframes.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-keyframes.html
new file mode 100644
index 0000000..6e50a84
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-keyframes.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<title>Viewport units in @keyframes</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+  iframe {
+    width: 200px;
+    height: 100px;
+  }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_interpolated_value(from, to, expected) {
+  test((t) => {
+    t.add_cleanup(() => { doc.body.innerHTML = ''; });
+    doc.body.innerHTML = `
+      <style>
+        @keyframes anim {
+          from { height: ${from}; }
+          to { height: ${to}}
+        }
+        div { animation: anim linear 10s -5s paused; }
+      </style>
+      <div></div>
+    `;
+    let div = doc.querySelector('div');
+    assert_equals(win.getComputedStyle(div).height, expected);
+  }, `Interpolation from ${from} to ${to} is ${expected} at 50%`);
+}
+
+test_interpolated_value('0px', '100vw', '100px');
+test_interpolated_value('0px', '100vi', '100px');
+test_interpolated_value('0px', '100vmax', '100px');
+test_interpolated_value('0px', '100svw', '100px');
+test_interpolated_value('0px', '100svi', '100px');
+test_interpolated_value('0px', '100svmax', '100px');
+test_interpolated_value('0px', '100lvw', '100px');
+test_interpolated_value('0px', '100lvi', '100px');
+test_interpolated_value('0px', '100lvmax', '100px');
+test_interpolated_value('0px', '100dvw', '100px');
+test_interpolated_value('0px', '100dvi', '100px');
+test_interpolated_value('0px', '100dvmax', '100px');
+
+test_interpolated_value('0px', '100vh', '50px');
+test_interpolated_value('0px', '100vb', '50px');
+test_interpolated_value('0px', '100vmin', '50px');
+test_interpolated_value('0px', '100svh', '50px');
+test_interpolated_value('0px', '100svb', '50px');
+test_interpolated_value('0px', '100svmin', '50px');
+test_interpolated_value('0px', '100lvh', '50px');
+test_interpolated_value('0px', '100lvb', '50px');
+test_interpolated_value('0px', '100lvmin', '50px');
+test_interpolated_value('0px', '100dvh', '50px');
+test_interpolated_value('0px', '100dvb', '50px');
+test_interpolated_value('0px', '100dvmin', '50px');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-media-queries.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-media-queries.html
new file mode 100644
index 0000000..33561793
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-media-queries.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<title>Viewport units in @media</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+  iframe {
+    width: 200px;
+    height: 100px;
+  }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_media_query(feature, result, description) {
+  test((t) => {
+    t.add_cleanup(() => { doc.body.innerHTML = ''; })
+    doc.body.innerHTML = `
+      <style>
+        body {
+          color: red;
+        }
+        @media (${feature}) {
+          body {
+            color: green;
+          }
+        }
+      </style>
+      `;
+    assert_equals(win.getComputedStyle(doc.body).color, result);
+  }, description);
+}
+
+function test_media_query_applies(feature) {
+  test_media_query(feature, 'rgb(0, 128, 0)', `@media(${feature}) applies`);
+}
+
+function test_media_query_does_not_apply(feature) {
+  test_media_query(feature, 'rgb(255, 0, 0)', `@media(${feature}) does not apply`);
+}
+
+test_media_query_applies('width:100vw');
+test_media_query_applies('width:100vi');
+test_media_query_applies('width:100vmax');
+test_media_query_applies('width:100svw');
+test_media_query_applies('width:100svi');
+test_media_query_applies('width:100svmax');
+test_media_query_applies('width:100lvw');
+test_media_query_applies('width:100lvi');
+test_media_query_applies('width:100lvmax');
+test_media_query_applies('width:100dvw');
+test_media_query_applies('width:100dvi');
+test_media_query_applies('width:100dvmax');
+
+test_media_query_applies('height:100vh');
+test_media_query_applies('height:100vb');
+test_media_query_applies('height:100vmin');
+test_media_query_applies('height:100svh');
+test_media_query_applies('height:100svb');
+test_media_query_applies('height:100svmin');
+test_media_query_applies('height:100lvh');
+test_media_query_applies('height:100lvb');
+test_media_query_applies('height:100lvmin');
+test_media_query_applies('height:100dvh');
+test_media_query_applies('height:100dvb');
+test_media_query_applies('height:100dvmin');
+
+test_media_query_does_not_apply('width:90vw');
+test_media_query_does_not_apply('height:90vh');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-parsing-expected.txt
deleted file mode 100644
index 3984c7c..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-parsing-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['width'] = "1vw" should set the property value
-PASS e.style['width'] = "1vh" should set the property value
-FAIL e.style['width'] = "1vi" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1vb" should set the property value assert_not_equals: property should be set got disallowed value ""
-PASS e.style['width'] = "1vmin" should set the property value
-PASS e.style['width'] = "1vmax" should set the property value
-FAIL e.style['width'] = "1svw" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1svh" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1svi" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1svb" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1svmin" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1svmax" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvw" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvh" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvi" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvb" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvmin" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1lvmax" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvw" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvh" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvi" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvb" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvmin" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['width'] = "1dvmax" should set the property value assert_not_equals: property should be set got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-writing-mode.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-writing-mode.html
new file mode 100644
index 0000000..ee851fd1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-units-writing-mode.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>Viewport units are responsive to writing-mode changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+  iframe {
+    width: 200px;
+    height: 100px;
+  }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_writing_mode(value, expected_pre, expected_post) {
+  test((t) => {
+    t.add_cleanup(() => { doc.body.innerHTML = ''; });
+    doc.body.innerHTML = `
+      <style>
+        div {
+          writing-mode: initial;
+          height: ${value};
+        }
+        .vertical {
+          writing-mode: vertical-rl;
+        }
+      </style>
+      <div></div>
+    `;
+    let div = doc.querySelector('div');
+    assert_equals(win.getComputedStyle(div).height, expected_pre);
+
+    // The writing-mode of the document element does not matter.
+    t.add_cleanup(() => { doc.documentElement.classList.remove('vertical'); })
+    doc.documentElement.classList.add('vertical');
+    assert_equals(win.getComputedStyle(div).height, expected_pre);
+
+    // The writing-mode of the target element is what matters.
+    div.classList.add('vertical');
+    assert_equals(win.getComputedStyle(div).height, expected_post);
+  }, `${value} computes to ${expected_post} with vertical writing-mode`);
+}
+
+test_writing_mode('100vi', '200px', '100px');
+test_writing_mode('100svi', '200px', '100px');
+test_writing_mode('100lvi', '200px', '100px');
+test_writing_mode('100dvi', '200px', '100px');
+
+test_writing_mode('100vb', '100px', '200px');
+test_writing_mode('100svb', '100px', '200px');
+test_writing_mode('100lvb', '100px', '200px');
+test_writing_mode('100dvb', '100px', '200px');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt
new file mode 100644
index 0000000..208239e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL BroadcastChannel messages aren't received from a cross-partition iframe assert_equals: expected "msg from iframe2" but got "msg from iframe1"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html
new file mode 100644
index 0000000..163e6c0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<!-- Pull in executor_path needed by newPopup / newIframe -->
+<script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script>
+<!-- Pull in newPopup / newIframe -->
+<script src="/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js"></script>
+<body>
+<script>
+
+const emit_script = (channel_name, message, done_queue_name) => `
+  const bc = new BroadcastChannel("${channel_name}");
+  bc.postMessage("${message}");
+  send("${done_queue_name}", "done");
+`;
+
+promise_test(async t => {
+  const origin = get_host_info().HTTPS_ORIGIN;
+  const not_same_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
+  const response_queue_uuid = token();
+
+  const popup_init_script = `
+    const importScript = ${importScript};
+    await importScript("/html/cross-origin-embedder-policy/credentialless" +
+                       "/resources/common.js");
+    await importScript("/html/cross-origin-embedder-policy/anonymous-iframe" +
+                       "/resources/common.js");
+    await importScript("/common/utils.js");
+    send("${response_queue_uuid}", newIframe("${origin}"));
+  `;
+
+  // Create a same-origin iframe in a cross-site popup.
+  const not_same_site_popup_uuid = newPopup(t, not_same_site_origin);
+  send(not_same_site_popup_uuid, popup_init_script);
+  const iframe_1_uuid = await receive(response_queue_uuid);
+
+  // Create a same-origin iframe in a same-site popup.
+  const same_origin_popup_uuid = newPopup(t, origin);
+  send(same_origin_popup_uuid, popup_init_script);
+  const iframe_2_uuid = await receive(response_queue_uuid);
+
+  const channel_name = token();
+  const bc = new BroadcastChannel(channel_name);
+  bc.onmessage = t.step_func(e => {
+    assert_equals(e.data, "msg from iframe2");
+    t.done();
+  });
+
+  // Instruct the not-same-top-level-site iframe to send a message on the BC
+  // channel we are listening on. This message should not be received since
+  // the iframe should be in a different partition.
+  send(iframe_1_uuid,
+       emit_script(channel_name, "msg from iframe1", response_queue_uuid));
+  assert_equals(await receive(response_queue_uuid), "done");
+
+  // Now instruct the same-top-level-site iframe to send a BC message. By
+  // the time we send the script to execute, have it send the BC message,
+  // and then receive the BC message in our BC instance, it should be
+  // reasonable to assume that the message from the first iframe would have
+  // been delivered if it was going to be.
+  send(iframe_2_uuid,
+       emit_script(channel_name, "msg from iframe2", response_queue_uuid));
+  assert_equals(await receive(response_queue_uuid), "done");
+
+}, "BroadcastChannel messages aren't received from a cross-partition iframe");
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/fledge_bidding_logic.js.php b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/fledge_bidding_logic.js.php
index dead6611..71d1b54 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/fledge_bidding_logic.js.php
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/fledge_bidding_logic.js.php
@@ -10,6 +10,7 @@
 function generateBid(
     interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
     browserSignals) {
+  console.log("generateBid running");
   return {'ad': 'example', 'bid': 1 + Number(interestGroup.name),
           'render': interestGroup.ads[0].renderUrl};
 }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-auction-worklet.js b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-auction-worklet.js
index 65a68d9..ac9b726 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-auction-worklet.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-auction-worklet.js
@@ -27,6 +27,13 @@
           testRunner.log(`Paused at ${topFrame.url} with reason "${reason}".`);
           targetSession.protocol.Debugger.resume();
         });
+        targetSession.protocol.Runtime.onConsoleAPICalled(event => {
+          const params = event.params;
+          const topFrame = params.stackTrace.callFrames[0];
+          testRunner.log(`Console call of type: ${params.type} ` +
+                         `arg0: ${params.args[0].value} ` +
+                         `at ${topFrame.url}:${topFrame.lineNumber}`);
+        });
 
         await targetSession.protocol.EventBreakpoints.setInstrumentationBreakpoint(
             {eventName: 'beforeBidderWorkletBiddingStart'});
diff --git a/third_party/blink/web_tests/virtual/basic-card-disabled/http/tests/payments/payment-instruments-expected.txt b/third_party/blink/web_tests/http/tests/payments/payment-instruments-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/basic-card-disabled/http/tests/payments/payment-instruments-expected.txt
rename to third_party/blink/web_tests/http/tests/payments/payment-instruments-expected.txt
diff --git a/third_party/blink/web_tests/virtual/basic-card-disabled/payments/payment-request-interface-expected.txt b/third_party/blink/web_tests/payments/payment-request-interface-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/basic-card-disabled/payments/payment-request-interface-expected.txt
rename to third_party/blink/web_tests/payments/payment-request-interface-expected.txt
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
index 7b3cf74..5aba13f3 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
index 5ab5a04..3ab20a38 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/system-color-picker-appearance/fast/forms/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/basic-card-disabled/README.md b/third_party/blink/web_tests/virtual/basic-card-disabled/README.md
deleted file mode 100644
index f6507ba..0000000
--- a/third_party/blink/web_tests/virtual/basic-card-disabled/README.md
+++ /dev/null
@@ -1 +0,0 @@
-The tests that depend on basic-card fail as expected when the basic-card feature is disabled.
diff --git a/third_party/blink/web_tests/virtual/basic-card-enabled/README.md b/third_party/blink/web_tests/virtual/basic-card-enabled/README.md
new file mode 100644
index 0000000..6be755fdd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/basic-card-enabled/README.md
@@ -0,0 +1,2 @@
+The tests in this directory that depend on basic-card succeed as expected when
+the basic-card feature is enabled.
diff --git a/third_party/blink/web_tests/virtual/basic-card-enabled/http/tests/payments/payment-instruments-expected.txt b/third_party/blink/web_tests/virtual/basic-card-enabled/http/tests/payments/payment-instruments-expected.txt
new file mode 100644
index 0000000..afedca9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/basic-card-enabled/http/tests/payments/payment-instruments-expected.txt
@@ -0,0 +1,17 @@
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+CONSOLE WARNING: The page that installed the payment handler does not contain a web app manifest link: <link rel="manifest" href="some-file-name-here">. This manifest defines the payment handler's name and icon. User may not recognize this payment handler in UI, because it will be labeled only by its origin.
+This is a testharness.js-based test.
+PASS PaymentInstruments set/get methods test with basic-card
+PASS PaymentInstruments set/get methods test with url method
+PASS PaymentInstruments.get() should throw NotFoundError if no stored key
+PASS PaymentInstruments delete method test
+PASS PaymentInstruments |has| method test
+PASS PaymentInstruments |keys| method test
+PASS PaymentInstruments |clear| method test
+PASS Throw NotAllowedError if permission is not granted
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/basic-card-enabled/payments/payment-request-interface-expected.txt b/third_party/blink/web_tests/virtual/basic-card-enabled/payments/payment-request-interface-expected.txt
new file mode 100644
index 0000000..9220e45e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/basic-card-enabled/payments/payment-request-interface-expected.txt
@@ -0,0 +1,150 @@
+CONSOLE ERROR: Uncaught (in promise) UnknownError: Renderer process could not establish or lost IPC connection to the PaymentRequest service in the browser process.
+CONSOLE ERROR: Uncaught (in promise) UnknownError: Renderer process could not establish or lost IPC connection to the PaymentRequest service in the browser process.
+CONSOLE ERROR: Uncaught (in promise) UnknownError: Renderer process could not establish or lost IPC connection to the PaymentRequest service in the browser process.
+CONSOLE ERROR: Uncaught (in promise) UnknownError: Renderer process could not establish or lost IPC connection to the PaymentRequest service in the browser process.
+This is a testharness.js-based test.
+PASS Creating a PaymentRequest with empty parameters should not throw or crash.
+PASS Creating a PaymentRequest with extra parameters should not throw or crash.
+PASS Creating a PaymentRequest with omitted optional parameters should not throw or crash.
+PASS Creating a PaymentRequest with undefined optional parameters should not throw or crash.
+PASS Creating a PaymentRequest with null optional parameters should not throw or crash.
+PASS PaymentRequest should have readonly shippingAddress and shippingOption properties.
+PASS PaymentRequest should have onShippingAddressChange and onShippingOptionChange events.
+PASS PaymentRequest should have methods abort() and show().
+PASS PaymentRequest.abort() and PaymentRequest.show() should take no parameters.
+PASS Valid data causes no errors.
+PASS Shipping option identifier should be null if shipping request is omitted.
+PASS Shipping option identifier should be null if shipping is explicitly not requested.
+PASS Shipping option identifier should be null if no shipping options are provided.
+PASS Shipping option identifier should be null if the single provided option is not selected.
+PASS Shipping option identifier should default to the single provided option if it is selected.
+PASS Shipping option identifier should be null if multiple unselected shipping options are provided.
+PASS Shipping option identifier should default to the selected shipping option.
+PASS Shipping option identifier should default to the last selected shipping option, if multiple are selected.
+PASS A TypeError should be thrown for duplicate shipping option identifiers.
+PASS Shipping type should be null if shipping is explicitly not requested.
+PASS Shipping type should be 'shipping' by default if shipping type isn't specified.
+PASS Shipping type should be null if shipping type is specified but requestShipping is false.
+PASS Shipping type should be 'shipping' if shipping type is specified as 'shipping'.
+PASS Shipping type should be 'delivery' if shipping type is specified as 'delivery'.
+PASS Shipping type should be 'pickup' if shipping type is specified as 'pickup'.
+PASS Shipping type should be 'shipping' if shipping type is specified as undefined.
+PASS Undefined display items should not throw.
+PASS Empty display items should not throw.
+PASS Non-negative total value should not throw.
+PASS Negative line item value should not throw.
+PASS Undefined modifiers should not throw.
+PASS Non-negative total value in PaymentDetailsModifier should not throw.
+PASS Duplicate supported payment method identifiers in separate methoData objects of modifiers should not throw.
+PASS Android Pay parameters for test environment with gateway token should not throw.
+PASS Android Pay parameters for produciton environment with network token should not throw.
+PASS Basic card parameters should not throw.
+PASS Empty basic card parameters should not throw.
+PASS Invalid basic card parameters should not throw when method name is not "basic-card".
+PASS Android Pay parameters for network token without environment key should not throw.
+PASS Invalid Android Pay parameters should not throw when method name is not "https://android.com/pay".
+PASS Invalid Android Pay parameters should not throw even when method name is "https://android.com/pay".
+PASS Array value for payment method specific data parameter should not throw
+PASS abort() without show() should reject with error
+PASS PaymentRequest constructor should throw for incorrect parameter types.
+PASS PaymentRequest constructor should throw for undefined required parameters.
+PASS PaymentRequest constructor should throw for null required parameter.
+PASS Empty list of supported payment method identifiers should throw TypeError.
+PASS Empty supported payment method identifier should throw RangeError.
+PASS Absence of total should throw TypeError.
+PASS Negative total value should throw a TypeError.
+PASS Negative total value in PaymentDetailsModifier should throw a TypeError.
+PASS Undefined supportedMethods in modifiers should throw TypeError.
+PASS Empty supportedMethods in modifiers should throw RangeError.
+PASS Absence of supportedMethods in modifiers should throw TypeError.
+PASS Empty details should throw
+PASS Null items should throw
+PASS Null shipping options should throw
+PASS Undefined PaymentShippingType value for shppingType should throw a TypeError
+PASS Null for shppingType should throw a TypeError
+PASS Array value for shppingType should throw a TypeError
+PASS Object value for shppingType should throw a TypeError
+PASS Numeric value for shppingType should throw a TypeError
+PASS String value for payment method specific data parameter should throw
+PASS Numeric value for payment method specific data parameter should throw
+PASS Infinite JSON value for one of the payment method specific data pieces should throw
+PASS Null for payment method specific data parameter should throw
+PASS Empty string for payment method specific data parameter should throw
+PASS PaymentRequest constructor should throw for invalid "basic-card" parameters.
+PASS Undefined currency code in total should throw
+PASS Invalid amount "-" in total should throw
+PASS Invalid amount "notdigits" in total should throw
+PASS Invalid amount "ALSONOTDIGITS" in total should throw
+PASS Invalid amount "10." in total should throw
+PASS Invalid amount ".99" in total should throw
+PASS Invalid amount "-10." in total should throw
+PASS Invalid amount "-.99" in total should throw
+PASS Invalid amount "10-" in total should throw
+PASS Invalid amount "1-0" in total should throw
+PASS Invalid amount "1.0.0" in total should throw
+PASS Invalid amount "1/3" in total should throw
+PASS Empty amount in total should throw
+PASS Null amount in total should throw
+PASS Undefined amount in total should throw
+PASS Undefined currency code in displayItems.0 should throw
+PASS Invalid amount "-" in displayItems.0 should throw
+PASS Invalid amount "notdigits" in displayItems.0 should throw
+PASS Invalid amount "ALSONOTDIGITS" in displayItems.0 should throw
+PASS Invalid amount "10." in displayItems.0 should throw
+PASS Invalid amount ".99" in displayItems.0 should throw
+PASS Invalid amount "-10." in displayItems.0 should throw
+PASS Invalid amount "-.99" in displayItems.0 should throw
+PASS Invalid amount "10-" in displayItems.0 should throw
+PASS Invalid amount "1-0" in displayItems.0 should throw
+PASS Invalid amount "1.0.0" in displayItems.0 should throw
+PASS Invalid amount "1/3" in displayItems.0 should throw
+PASS Empty amount in displayItems.0 should throw
+PASS Null amount in displayItems.0 should throw
+PASS Undefined amount in displayItems.0 should throw
+PASS Undefined currency code in shippingOptions.0 should throw
+PASS Invalid amount "-" in shippingOptions.0 should throw
+PASS Invalid amount "notdigits" in shippingOptions.0 should throw
+PASS Invalid amount "ALSONOTDIGITS" in shippingOptions.0 should throw
+PASS Invalid amount "10." in shippingOptions.0 should throw
+PASS Invalid amount ".99" in shippingOptions.0 should throw
+PASS Invalid amount "-10." in shippingOptions.0 should throw
+PASS Invalid amount "-.99" in shippingOptions.0 should throw
+PASS Invalid amount "10-" in shippingOptions.0 should throw
+PASS Invalid amount "1-0" in shippingOptions.0 should throw
+PASS Invalid amount "1.0.0" in shippingOptions.0 should throw
+PASS Invalid amount "1/3" in shippingOptions.0 should throw
+PASS Empty amount in shippingOptions.0 should throw
+PASS Null amount in shippingOptions.0 should throw
+PASS Undefined amount in shippingOptions.0 should throw
+PASS Undefined currency code in modifiers.0.total should throw
+PASS Invalid amount "-" in modifiers.0.total should throw
+PASS Invalid amount "notdigits" in modifiers.0.total should throw
+PASS Invalid amount "ALSONOTDIGITS" in modifiers.0.total should throw
+PASS Invalid amount "10." in modifiers.0.total should throw
+PASS Invalid amount ".99" in modifiers.0.total should throw
+PASS Invalid amount "-10." in modifiers.0.total should throw
+PASS Invalid amount "-.99" in modifiers.0.total should throw
+PASS Invalid amount "10-" in modifiers.0.total should throw
+PASS Invalid amount "1-0" in modifiers.0.total should throw
+PASS Invalid amount "1.0.0" in modifiers.0.total should throw
+PASS Invalid amount "1/3" in modifiers.0.total should throw
+PASS Empty amount in modifiers.0.total should throw
+PASS Null amount in modifiers.0.total should throw
+PASS Undefined amount in modifiers.0.total should throw
+PASS Undefined currency code in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "-" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "notdigits" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "ALSONOTDIGITS" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "10." in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount ".99" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "-10." in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "-.99" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "10-" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "1-0" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "1.0.0" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Invalid amount "1/3" in modifiers.0.additionalDisplayItems.0 should throw
+PASS Empty amount in modifiers.0.additionalDisplayItems.0 should throw
+PASS Null amount in modifiers.0.additionalDisplayItems.0 should throw
+PASS Undefined amount in modifiers.0.additionalDisplayItems.0 should throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/target/target-auction-worklet-expected.txt b/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/target/target-auction-worklet-expected.txt
index e7543f5..b9d7d36 100644
--- a/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/target/target-auction-worklet-expected.txt
+++ b/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/target/target-auction-worklet-expected.txt
@@ -5,7 +5,9 @@
 Attached to target https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
   Bidder instrumentation breakpoint set attempted
 Paused at https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php with reason "EventListener".
+Console call of type: log arg0: generateBid running at https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php:8
 Paused at https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php with reason "EventListener".
+Console call of type: log arg0: generateBid running at https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php:8
 Attached to target https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
   Bidder instrumentation breakpoint set attempted
 Auction winner:urn:uuid:(randomized)
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt
new file mode 100644
index 0000000..7daa7eab
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt
@@ -0,0 +1,2 @@
+This suite runs BroadcastChannel tests with ThirdPartyStoragePartitioning
+enabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt
new file mode 100644
index 0000000..e822216
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS BroadcastChannel messages aren't received from a cross-partition iframe
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 5601c64..1405c791 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -11371,6 +11371,12 @@
     method dpcm
     method dpi
     method dppx
+    method dvb
+    method dvh
+    method dvi
+    method dvmax
+    method dvmin
+    method dvw
     method em
     method escape
     method ex
@@ -11378,6 +11384,12 @@
     method grad
     method in
     method kHz
+    method lvb
+    method lvh
+    method lvi
+    method lvmax
+    method lvmin
+    method lvw
     method mm
     method ms
     method number
@@ -11396,8 +11408,16 @@
     method rem
     method s
     method supports
+    method svb
+    method svh
+    method svi
+    method svmax
+    method svmin
+    method svw
     method turn
+    method vb
     method vh
+    method vi
     method vmax
     method vmin
     method vw
diff --git a/third_party/dom_distiller_js/README.chromium b/third_party/dom_distiller_js/README.chromium
index ed9649d..13dc5de 100644
--- a/third_party/dom_distiller_js/README.chromium
+++ b/third_party/dom_distiller_js/README.chromium
@@ -1,6 +1,6 @@
 Name: dom-distiller-js
 URL: https://github.com/chromium/dom-distiller
-Version: 3eef8a0ce4
+Version: 36c7178512
 License: BSD
 Security Critical: yes
 
@@ -8,7 +8,21 @@
 Dom Distiller is a java library that extracts article content from a web page.
 It is compiled to javascript with GWT.
 
-How to update:
-Run update_domdistiller_js.sh.
+Requirements for updating:
+- ant
+- OpenJDK 8
+
+Updating:
+From the root of a Chrome checkout, run something like:
+
+PATH=$PWD/out/release:$PATH \
+  PYTHONPATH=$PWD/third_party/protobuf/python:$PWD/out/release/pyproto \
+  JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 \
+  third_party/dom_distiller_js/update_domdistiller_js.sh
+
+This should ensure that the update script:
+- uses the protoc and protobuf Python libraries from //third_party/protobuf
+  to avoid errors like "ImportError: No module named protobuf.descriptor_pb2"
+- uses OpenJDK 8 since GWT does not support newer Java versions.
 
 Local Modifications: None
diff --git a/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py b/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
index 3921a46..5a6fead 100755
--- a/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
+++ b/third_party/dom_distiller_js/protoc_plugins/json_values_converter.py
@@ -175,7 +175,7 @@
         'if (!value->is_list()) {{\n'
         '  goto error;\n'
         '}}\n'
-        'for (const auto& element : value->GetList()) {{\n'
+        'for (const auto& element : value->GetListDeprecated()) {{\n'
     )
 
     with self.AddIndent():
diff --git a/third_party/dom_distiller_js/test_sample_json_converter.h.golden b/third_party/dom_distiller_js/test_sample_json_converter.h.golden
index 3c7faaf0..8b7bfa2 100644
--- a/third_party/dom_distiller_js/test_sample_json_converter.h.golden
+++ b/third_party/dom_distiller_js/test_sample_json_converter.h.golden
@@ -119,7 +119,7 @@
                 if (!value->is_list()) {
                   goto error;
                 }
-                for (const auto& element : value->GetList()) {
+                for (const auto& element : value->GetListDeprecated()) {
                   if (!element.is_bool()) {
                     goto error;
                   }
@@ -151,7 +151,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!(element.is_int() || element.is_double())) {
                   goto error;
                 }
@@ -162,7 +162,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!(element.is_int() || element.is_double())) {
                   goto error;
                 }
@@ -173,7 +173,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!element.is_int()) {
                   goto error;
                 }
@@ -184,7 +184,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!element.is_bool()) {
                   goto error;
                 }
@@ -195,7 +195,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!element.is_string()) {
                   goto error;
                 }
@@ -206,7 +206,7 @@
               if (!value->is_list()) {
                 goto error;
               }
-              for (const auto& element : value->GetList()) {
+              for (const auto& element : value->GetListDeprecated()) {
                 if (!dom_distiller::test_sample::proto::json::Repeated::Message::ReadFromValue(element, message->add_message_value())) {
                   goto error;
                 }
diff --git a/third_party/dom_distiller_js/update_domdistiller_js.sh b/third_party/dom_distiller_js/update_domdistiller_js.sh
index 4fcf3ad..4cd40433 100755
--- a/third_party/dom_distiller_js/update_domdistiller_js.sh
+++ b/third_party/dom_distiller_js/update_domdistiller_js.sh
@@ -100,7 +100,7 @@
     gen_message > $message
 
     git commit -a -F $message
-    git push origin master:refs/for/master
+    git push origin main:refs/for/main
   else
     # No changes to external repo, but need to check if DEPS refers to same SHA1.
     if [[ -n "$gerrit_url" ]]; then
diff --git a/third_party/liburlpattern/pattern.cc b/third_party/liburlpattern/pattern.cc
index 2b53ff79e..9f8162f 100644
--- a/third_party/liburlpattern/pattern.cc
+++ b/third_party/liburlpattern/pattern.cc
@@ -463,7 +463,8 @@
 
 bool Pattern::DirectMatch(
     absl::string_view input,
-    std::vector<std::pair<absl::string_view, absl::string_view>>*
+    std::vector<
+        std::pair<absl::string_view, absl::optional<absl::string_view>>>*
         group_list_out) const {
   ABSL_ASSERT(CanDirectMatch());
 
diff --git a/third_party/liburlpattern/pattern.h b/third_party/liburlpattern/pattern.h
index f522f44c..88076af8 100644
--- a/third_party/liburlpattern/pattern.h
+++ b/third_party/liburlpattern/pattern.h
@@ -11,6 +11,7 @@
 #include <vector>
 #include "base/component_export.h"
 #include "third_party/abseil-cpp/absl/strings/string_view.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/liburlpattern/options.h"
 
 namespace liburlpattern {
@@ -144,9 +145,12 @@
   //
   // `DirectMatch()` returns true if the pattern matches `input` and false
   // otherwise.  If the `group_list_out` pointer is provided then the vector
-  // is populated with name:value pairs for matched pattern groups.
+  // is populated with name:value pairs for matched pattern groups.  If a
+  // group had an optional modifier and it did not match any input characters
+  // then its `group_list_out` value will be std::nullopt.
   bool DirectMatch(absl::string_view input,
-                   std::vector<std::pair<absl::string_view, absl::string_view>>*
+                   std::vector<std::pair<absl::string_view,
+                                         absl::optional<absl::string_view>>>*
                        group_list_out) const;
 
  private:
diff --git a/third_party/liburlpattern/pattern_unittest.cc b/third_party/liburlpattern/pattern_unittest.cc
index 4e4861a..35cb413 100644
--- a/third_party/liburlpattern/pattern_unittest.cc
+++ b/third_party/liburlpattern/pattern_unittest.cc
@@ -446,7 +446,8 @@
 struct DirectMatchCase {
   absl::string_view input;
   bool expected_match = true;
-  std::vector<std::pair<absl::string_view, absl::string_view>> expected_groups;
+  std::vector<std::pair<absl::string_view, absl::optional<absl::string_view>>>
+      expected_groups;
 };
 
 void RunDirectMatchTest(absl::string_view input,
@@ -458,7 +459,8 @@
   auto& pattern = result.value();
   EXPECT_TRUE(pattern.CanDirectMatch());
   for (const auto& c : case_list) {
-    std::vector<std::pair<absl::string_view, absl::string_view>> matched_groups;
+    std::vector<std::pair<absl::string_view, absl::optional<absl::string_view>>>
+        matched_groups;
     EXPECT_EQ(c.expected_match, pattern.DirectMatch(c.input, &matched_groups));
     ASSERT_EQ(c.expected_groups.size(), matched_groups.size());
     for (size_t i = 0; i < matched_groups.size(); ++i) {
diff --git a/third_party/rust/cfg_if/v1/BUILD.gn b/third_party/rust/cfg_if/v1/BUILD.gn
index bc884efb..c6570b7 100644
--- a/third_party/rust/cfg_if/v1/BUILD.gn
+++ b/third_party/rust/cfg_if/v1/BUILD.gn
@@ -14,7 +14,7 @@
   visibility = [ "//third_party/rust/*" ]
   crate_root = "crate/src/lib.rs"
 
-  # Unit tests skipped. Generate with --with-tests to include them
+  # Unit tests skipped. Generate with --with-tests to include them.
   skip_unit_tests = true
   sources = [ "crate/src/lib.rs" ]
   edition = "2018"
diff --git a/third_party/rust/rstest/v0_12/BUILD.gn b/third_party/rust/rstest/v0_12/BUILD.gn
new file mode 100644
index 0000000..5b4ee97
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2022 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("//build/rust/cargo_crate.gni")
+
+cargo_crate("test_support") {
+  crate_name = "rstest"
+  epoch = "0.12"
+  crate_type = "proc-macro"
+  testonly = "true"
+  crate_root = "crate/src/lib.rs"
+  skip_unit_tests = true
+  sources = [ "crate/src/lib.rs" ]
+  edition = "2018"
+  deps = [
+    "//third_party/rust/cfg_if/v1:lib",
+    "//third_party/rust/proc_macro2/v1:lib",
+    "//third_party/rust/quote/v1:lib",
+    "//third_party/rust/syn/v1:lib",
+  ]
+  build_deps = [ "//third_party/rust/rustc_version/v0_4:buildrs_support" ]
+  build_root = "crate/build.rs"
+  build_sources = [ "crate/build.rs" ]
+}
diff --git a/third_party/rust/rstest/v0_12/README.chromium b/third_party/rust/rstest/v0_12/README.chromium
new file mode 100644
index 0000000..33915306
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/README.chromium
@@ -0,0 +1,7 @@
+Name: rstest
+URL: https://crates.io/crates/rstest
+Description: Rust fixture based test framework. It use procedural macro
+to implement fixtures and table based tests.
+Version: 0.12.0
+Security Critical: no
+License: Apache 2.0
diff --git a/third_party/rust/rstest/v0_12/crate/.cargo_vcs_info.json b/third_party/rust/rstest/v0_12/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..91ee11a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "2e3a3d5381e1923378ace72724fc6dab8b1ac188"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/.github/workflows/test.yml b/third_party/rust/rstest/v0_12/crate/.github/workflows/test.yml
new file mode 100644
index 0000000..7f830627
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/.github/workflows/test.yml
@@ -0,0 +1,24 @@
+name: Test
+
+on: 
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+  release:
+    types: # This configuration does not affect the page_build event above
+      - created
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: Build
+      run: cargo build --all --verbose
+    - name: Run tests
+      run: cargo test --all --verbose
diff --git a/third_party/rust/rstest/v0_12/crate/.github/workflows/update_head_docs.yml b/third_party/rust/rstest/v0_12/crate/.github/workflows/update_head_docs.yml
new file mode 100644
index 0000000..8dc5944
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/.github/workflows/update_head_docs.yml
@@ -0,0 +1,32 @@
+name: Update Head Docs
+
+on: [repository_dispatch, workflow_dispatch]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: Install latest nightly
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: nightly
+    - name: Build Docs
+      uses: actions-rs/cargo@v1
+      with: 
+        command: doc
+        toolchain: nightly
+        args: --no-deps
+    - name: Copy docs
+      run: cp -ra target/doc/* docs/head/
+    - name: Commit files
+      run: |
+        git config --local user.email "action@github.com"
+        git config --local user.name "GitHub Action"
+        git commit -m "Update docs" -a
+    - name: Push changes
+      uses: ad-m/github-push-action@master
+      with:
+        github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/third_party/rust/rstest/v0_12/crate/.gitignore b/third_party/rust/rstest/v0_12/crate/.gitignore
new file mode 100644
index 0000000..0362446
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/.gitignore
@@ -0,0 +1,5 @@
+/.idea
+target
+**/*.rs.bk
+Cargo.lock
+*.iml
diff --git a/third_party/rust/rstest/v0_12/crate/.rustfmt.toml b/third_party/rust/rstest/v0_12/crate/.rustfmt.toml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/.rustfmt.toml
diff --git a/third_party/rust/rstest/v0_12/crate/CHANGELOG.md b/third_party/rust/rstest/v0_12/crate/CHANGELOG.md
new file mode 100644
index 0000000..1d3f4183a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/CHANGELOG.md
@@ -0,0 +1,213 @@
+# Changelog
+
+## [0.12.0] 2021/12/12
+
+### Add
+
+- Add `#[once]` fixture attribute to create static fixtures (See #119)
+
+### Fixed
+
+- Fixed check of available features before to enable macro diagnostic (See #126)
+
+## [0.11.0] 2021/08/01
+
+### Fixed
+
+- use mutable fixture in in cases and value list (See #121)
+
+## [0.10.0] 2021/05/16
+
+### Add
+
+- Rename fixture (See #107 and #108)  
+
+### Fixed
+
+- Wired behaviour in `#[fixture]` with generics types that have transitive
+reference (See #116)  
+
+## [0.9.0] 2021/05/2
+
+### Add
+
+- `#[future]` arg attribute to remove `impl Future<>` boilerplate. (See #98) 
+
+## [0.8.0] 2021/4/25
+
+### Add
+
+- Magic Conversion: use literal string for define values where type implements 
+`FromStr` trait (See #111)
+
+### Changed
+
+- `#[default]` arg attribute cannot use key = arbitrary rust expression syntax 
+(is unstable https://github.com/rust-lang/rust/issues/78835). So we switched
+to `#[default(expression)]` syntax. (See #117) 
+
+### Fixed
+
+- #117 introduced an unstable syntax
+
+## [0.7.0] 2021/3/21
+
+This version intruduce the new more composable syntax. And async 
+fixtures (thanks to @rubdos)
+
+### Add
+- New syntax that leverage on function and argument attributes
+to implement all features (See #99, #100, #101, #103, #109 and #102)
+- `async` fixtures (See #86, #96. Thanks to @rubdos).
+
+### Changed
+- Moved integration tests resouces in `test` directory (See #97)
+
+## [0.6.4] 2020/6/20
+
+### Add
+- Implemented reusable test list with `rstest_reuse` external crate (See #80)
+
+## [0.6.3] 2020/4/19
+
+### Add
+- Define default values instead use trivial fixtures (See #72).
+
+## [0.6.2] 2020/4/13
+
+### Add
+- Injecting test attribute. You can choose your own test attribute (should be something like `*::test`) 
+for each test. This feature enable every async runtime (See #91).
+
+### Changed
+- Start to use `rstest` to test `rstest` (On going task #92) 
+
+## [0.6.1] 2020/4/5
+
+### Add
+- Introducing async tests support. Leverage on `async_std::test` to automatically switch to
+async test (both for single, cases and matrix) (See #73)
+
+## [0.6.0] 2020/3/5
+
+### Add
+- Hook argument name to fixture by remove starting `_` (See #70)
+- Every `case` can have a specific set of attributes (See #82)
+
+### Changed
+
+- Removed useless `rstest_parametrize` and `rstest_matrix` (See #81). From 0.5.0 you
+can use just `rstest` to create cases and values list.
+- Removed `cargo-edit` test dependecy (See #61)
+
+## [0.5.3] 2020/1/23
+
+### Fixed
+
+- Fixed  a false _unused mut_ warning regression introduced by
+partial fixtures (See [8a0ff08](https://github.com/la10736/rstest/commit/8a0ff0874dc8186edfaefb1ddef64d53666b94da))
+
+## [0.5.2] 2019/12/29
+
+### Fixed
+
+- Fixed _unused attribute_ warning when use `should_panic`
+attribute (See #79)
+
+## [0.5.1] 2019/12/14
+
+### Fixed
+
+- `README.md` links
+- License files
+
+## [0.5.0] 2019/12/13
+
+### Added
+
+- Use just `rstest` for implementing all kind of tests (See #42)
+- New matrix tests render: indicate argument name and nest groups
+in modules (See #68 for details)
+- CI (github actions) build and tests (See #46)
+
+### Changed
+
+- Better `README.md` that introduce all features
+- (From rustc 1.40) Deprecated `rstest_parametrize` and `rstest_matrix`:
+`rstest` now cover all features
+- Refactored
+
+### Fixed
+
+- Error message if fixture or value are used more than once
+
+## [0.4.1] 2019-10-05
+
+### Changed
+
+- Fixed README, crate description, changelog dates
+
+## [0.4.0] 2019-10-04
+
+### Added
+
+- Injecting fixture with partial values in all tests (See #48)
+- Add new `rstest_matrix` macro to build tests by carthesian product of
+input arguments (See #38)
+
+### Fixed
+
+- Just bugs in tests
+
+### Changed
+
+- Use `unindent` crate instead the home made `Deindent` trait
+- Use `itertools`
+- Refactor parsing
+
+## [0.3.0] 2019-06-28
+
+### Added
+
+- Introduced `fixture` macro: Now you must annotate your fixture by
+this tag. See #5
+- Support for arbitrary rust code without use `Unwrap(str_lit)` trick.
+See #19 and #20 (deprecate `Unwrap()`)
+- Support for tests that return `Result()`, See #23
+- Support for dump test arguments
+- `rstest_parametrize` use module to group cases (See #13)
+- You can optionally give a an name for each test case (See #11)
+- Docs
+- Descriptive error handling: See #12, #15
+- `rstest_parametrize` can leave comma after last case
+
+### Fixed
+
+- `rstest_parametrize` should catch error in input! See #1, #14
+- Use negative literal. See #18
+
+### Changed
+
+- You need to use `fixture` to tag all your fixtures.
+- Migrate to 2018 Epoch
+- Tests: Refactoring and speed up
+
+## [0.2.2] 2018-10-18
+
+### Fixed
+
+- Better error handling
+
+## [0.2.1] 2018-10-15
+
+### Changed
+
+- crate.io categories
+
+## [0.2.0] 2018-10-14
+
+First Public release.
+
+## [0.1.0] ...
+
+Just my testing and private use.
diff --git a/third_party/rust/rstest/v0_12/crate/Cargo.toml b/third_party/rust/rstest/v0_12/crate/Cargo.toml
new file mode 100644
index 0000000..ea6a136
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/Cargo.toml
@@ -0,0 +1,69 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "rstest"
+version = "0.12.0"
+authors = ["Michele d'Amico <michele.damico@gmail.com>"]
+exclude = ["/playground", "/rstest_fixtures"]
+description = "Rust fixture based test framework. It use procedural macro\nto implement fixtures and table based tests.\n"
+homepage = "https://github.com/la10736/rstest"
+readme = "README.md"
+keywords = ["test", "fixture"]
+categories = ["development-tools::testing"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/la10736/rstest"
+
+[lib]
+proc-macro = true
+[dependencies.cfg-if]
+version = "1.0.0"
+
+[dependencies.proc-macro2]
+version = "1.0.27"
+
+[dependencies.quote]
+version = "1.0.9"
+
+[dependencies.syn]
+version = "1.0.72"
+features = ["full", "parsing", "extra-traits", "visit", "visit-mut"]
+[dev-dependencies.actix-rt]
+version = "2.2.0"
+
+[dev-dependencies.async-std]
+version = "1.9.0"
+features = ["attributes"]
+
+[dev-dependencies.lazy_static]
+version = "1.4.0"
+
+[dev-dependencies.mytest]
+version = "0.11.0"
+package = "rstest"
+
+[dev-dependencies.pretty_assertions]
+version = "1.0.0"
+
+[dev-dependencies.rstest_reuse]
+version = "0.1.3"
+
+[dev-dependencies.rstest_test]
+version = "0.5.0"
+
+[dev-dependencies.temp_testdir]
+version = "0.2.3"
+
+[dev-dependencies.unindent]
+version = "0.1.7"
+[build-dependencies.rustc_version]
+version = "0.4.0"
diff --git a/third_party/rust/rstest/v0_12/crate/Cargo.toml.orig b/third_party/rust/rstest/v0_12/crate/Cargo.toml.orig
new file mode 100644
index 0000000..f8236ba
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/Cargo.toml.orig
@@ -0,0 +1,51 @@
+[package]
+authors = ["Michele d'Amico <michele.damico@gmail.com>"]
+categories = ["development-tools::testing"]
+description = """
+Rust fixture based test framework. It use procedural macro
+to implement fixtures and table based tests.
+"""
+edition = "2018"
+exclude = [
+  "/playground",
+  "/rstest_fixtures",
+]
+homepage = "https://github.com/la10736/rstest"
+keywords = ["test", "fixture"]
+license = "MIT/Apache-2.0"
+name = "rstest"
+readme = "README.md"
+repository = "https://github.com/la10736/rstest"
+version = "0.12.0"
+
+[workspace]
+exclude = [
+  "playground",
+]
+members = [
+  "rstest_test",
+  "rstest_reuse",
+]
+
+[lib]
+proc-macro = true
+
+[dependencies]
+cfg-if = "1.0.0"
+proc-macro2 = "1.0.27"
+quote = "1.0.9"
+syn = {version = "1.0.72", features = ["full", "parsing", "extra-traits", "visit", "visit-mut"]}
+
+[dev-dependencies]
+actix-rt = "2.2.0"
+async-std = {version = "1.9.0", features = ["attributes"]}
+lazy_static = "1.4.0"
+mytest = {package = "rstest", version = "0.11.0"}
+pretty_assertions = "1.0.0"
+rstest_reuse = "0.1.3"
+rstest_test = "0.5.0"
+temp_testdir = "0.2.3"
+unindent = "0.1.7"
+
+[build-dependencies]
+rustc_version = "0.4.0"
diff --git a/third_party/rust/rstest/v0_12/crate/LICENCE-MIT b/third_party/rust/rstest/v0_12/crate/LICENCE-MIT
new file mode 100644
index 0000000..b1d8b47
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/LICENCE-MIT
@@ -0,0 +1,18 @@
+Copyright 2018-19 Michele d'Amico
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), 
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/LICENSE-APACHE b/third_party/rust/rstest/v0_12/crate/LICENSE-APACHE
new file mode 100644
index 0000000..5bb8986
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/LICENSE-APACHE
@@ -0,0 +1,13 @@
+Copyright 2018-19 Michele d'Amico
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/README.md b/third_party/rust/rstest/v0_12/crate/README.md
new file mode 100644
index 0000000..8214ce87
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/README.md
@@ -0,0 +1,390 @@
+[![Crate][crate-image]][crate-link]
+[![Docs][docs-image]][docs-link]
+[![Status][test-action-image]][test-action-link]
+[![Apache 2.0 Licensed][license-apache-image]][license-apache-link]
+[![MIT Licensed][license-mit-image]][license-mit-link]
+
+# Fixture-based test framework for Rust
+
+## Introduction
+
+`rstest` uses procedural macros to help you on writing
+fixtures and table-based tests. To use it, add the
+following lines to your `Cargo.toml` file:
+
+```
+[dev-dependencies]
+rstest = "0.12.0"
+```
+
+### Fixture
+
+The core idea is that you can inject your test dependencies
+by passing them as test arguments. In the following example,
+a `fixture` is defined and then used in two tests,
+simply providing it as an argument:
+
+```rust
+use rstest::*;
+
+#[fixture]
+pub fn fixture() -> u32 { 42 }
+
+#[rstest]
+fn should_success(fixture: u32) {
+    assert_eq!(fixture, 42);
+}
+
+#[rstest]
+fn should_fail(fixture: u32) {
+    assert_ne!(fixture, 42);
+}
+```
+
+### Parametrize
+
+You can also inject values in some other ways. For instance, you can
+create a set of tests by simply providing the injected values for each
+case: `rstest` will generate an independent test for each case.
+
+```rust
+use rstest::rstest;
+
+#[rstest]
+#[case(0, 0)]
+#[case(1, 1)]
+#[case(2, 1)]
+#[case(3, 2)]
+#[case(4, 3)]
+fn fibonacci_test(#[case] input: u32, #[case] expected: u32) {
+    assert_eq!(expected, fibonacci(input))
+}
+```
+
+Running `cargo test` in this case executes five tests:
+
+```bash
+running 5 tests
+test fibonacci_test::case_1 ... ok
+test fibonacci_test::case_2 ... ok
+test fibonacci_test::case_3 ... ok
+test fibonacci_test::case_4 ... ok
+test fibonacci_test::case_5 ... ok
+
+test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+If you need to just providing a bunch of values for which you
+need to run your test, you can use `#[values(list, of, values)]`
+argument attribute:
+
+```rust
+use rstest::rstest;
+
+#[rstest]
+fn should_be_invalid(
+    #[values(None, Some(""), Some("    "))]
+    value: Option<&str>
+) {
+    assert!(!valid(value))
+}
+```
+
+Or create a _matrix_ test by using _list of values_ for some
+variables that will generate the cartesian product of all the
+values.
+
+#### Use Parametrize definition in more tests
+
+If you need to use a test list for more than one test you can use [`rstest_reuse`][reuse-crate-link] 
+crate. With this helper crate you can define a template and use it everywhere .
+
+```rust
+use rstest::rstest;
+use rstest_reuse::{self, *};
+
+#[template]
+#[rstest]
+#[case(2, 2)]
+#[case(4/2, 2)]
+fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
+
+#[apply(two_simple_cases)]
+fn it_works(#[case] a: u32, #[case] b: u32) {
+    assert!(a == b);
+}
+```
+
+See [`rstest_reuse`][reuse-crate-link] for more dettails.
+
+### Magic Conversion
+
+If you need a value where its type implement `FromStr()` trait you can use a literal 
+string to build it:
+
+```rust
+# use rstest::rstest;
+# use std::net::SocketAddr;
+#[rstest]
+#[case("1.2.3.4:8080", 8080)]
+#[case("127.0.0.1:9000", 9000)]
+fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) {
+    assert_eq!(expected, addr.port());
+}
+```
+You can use this feature also in value list and in fixture default value.
+
+### Async
+
+`rstest` provides out of the box `async` support. Just mark your
+test function as `async` and it'll use `#[async-std::test]` to
+annotate it. This feature can be really useful to build async
+parametric tests using a tidy syntax:
+
+```rust
+use rstest::*;
+
+#[rstest]
+#[case(5, 2, 3)]
+#[should_panic]
+#[case(42, 40, 1)]
+async fn my_async_test(#[case] expected: u32, #[case] a: u32, #[case] b: u32) {
+    assert_eq!(expected, async_sum(a, b).await);
+}
+```
+Currently only `async-std` is supported out of the box. But if you need to use
+another runtime that provide it's own test attribute (i.e. `tokio::test` or
+`actix_rt::test`) you can use it in your `async` test like described in
+[Inject Test Attribute](#inject-test-attribute).
+
+To use this feature, you need to enable `attributes` in the `async-std`
+features list in your `Cargo.toml`:
+
+```toml
+async-std = { version = "1.5", features = ["attributes"] }
+```
+
+If your test input is an async value (fixture or test parameter) you can use `#[future]`
+attribute to remove `impl Future<Output = T>` boilerplate and just use `T`:
+
+```rust
+use rstest::*;
+#[fixture]
+async fn base() -> u32 { 42 }
+
+#[rstest]
+#[case(21, async { 2 })]
+#[case(6, async { 7 })]
+async fn my_async_test(#[future] base: u32, #[case] expected: u32, #[future] #[case] div: u32) {
+    assert_eq!(expected, base.await / div.await);
+}
+```
+
+### Inject Test Attribute
+
+If you would like to use another `test` attribute for your test you can simply 
+indicate it in your test function's attributes. For instance if you want
+to test some async function with use `actix_rt::test` attribute you can just write:
+
+```rust
+use rstest::*;
+use actix_rt;
+use std::future::Future;
+
+#[rstest]
+#[case(2, async { 4 })]
+#[case(21, async { 42 })]
+#[actix_rt::test]
+async fn my_async_test(#[case] a: u32, result: #[case] #[future] u32) {
+    assert_eq!(2 * a, result.await);
+}
+```
+Just the attributes that ends with `test` (last path segment) can be injected.
+
+### Use `#[once]` Fixture
+
+If you need to a fixture that should be inizialized just once for all tests
+you can use `#[once]` attribute. `rstest` call your fixture function just once and
+return a reference to your function result to all your tests:
+
+```rust
+#[fixture]
+#[once]
+fn once_fixture() -> i32 { 42 }
+
+#[rstest]
+fn single(once_fixture: &i32) {
+    // All tests that use once_fixture will share the same reference to once_fixture() 
+    // function result.
+    assert_eq!(&42, once_fixture)
+}
+```
+
+
+## Complete Example
+
+All these features can be used together with a mixture of fixture variables,
+fixed cases and bunch of values. For instance, you might need two
+test cases which test for panics, one for a logged in user and one for a guest user.
+
+```rust
+use rstest::*;
+
+#[fixture]
+fn repository() -> InMemoryRepository {
+    let mut r = InMemoryRepository::default();
+    // fill repository with some data
+    r
+}
+
+#[fixture]
+fn alice() -> User {
+    User::logged("Alice", "2001-10-04", "London", "UK")
+}
+
+#[rstest]
+#[case::authed_user(alice())] // We can use `fixture` also as standard function
+#[case::guest(User::Guest)]   // We can give a name to every case : `guest` in this case
+                              // and `authed_user`
+#[should_panic(expected = "Invalid query error")] // We whould test a panic
+fn should_be_invalid_query_error(
+    repository: impl Repository, 
+    #[case] user: User, 
+    #[values("     ", "^%$#@!", "....")]
+    query: &str
+) {
+    repository.find_items(&user, query).unwrap();
+}
+```
+
+This example will generate exactly 6 tests grouped by 2 different cases:
+
+```
+running 6 tests
+test should_be_invalid_query_error::case_1_authed_user::query_1 ... ok
+test should_be_invalid_query_error::case_2_guest::query_2 ... ok
+test should_be_invalid_query_error::case_2_guest::query_3 ... ok
+test should_be_invalid_query_error::case_1_authed_user::query_2 ... ok
+test should_be_invalid_query_error::case_1_authed_user::query_3 ... ok
+test should_be_invalid_query_error::case_2_guest::query_1 ... ok
+
+test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+## More
+
+Is that all? Not quite yet!
+
+A fixture can be injected by another fixture and they can be called
+using just some of its arguments.
+
+```rust
+#[fixture]
+fn user(#[default("Alice")] name: &str, #[default(22)] age: u8) -> User {
+    User::new(name, age)
+}
+
+#[rstest]
+fn is_alice(user: User) {
+    assert_eq!(user.name(), "Alice")
+}
+
+#[rstest]
+fn is_22(user: User) {
+    assert_eq!(user.age(), 22)
+}
+
+#[rstest]
+fn is_bob(#[with("Bob")] user: User) {
+    assert_eq!(user.name(), "Bob")
+}
+
+#[rstest]
+fn is_42(#[with("", 42)] user: User) {
+    assert_eq!(user.age(), 42)
+}
+```
+
+As you noted you can provide default values without the need of a fixture
+to define it.
+
+Finally if you need tracing the input values you can just
+add the `trace` attribute to your test to enable the dump of all input
+variables.
+
+```rust
+#[rstest]
+#[case(42, "FortyTwo", ("minus twelve", -12))]
+#[case(24, "TwentyFour", ("minus twentyfour", -24))]
+#[trace] //This attribute enable traceing
+fn should_fail(#[case] number: u32, #[case] name: &str, #[case] tuple: (&str, i32)) {
+    assert!(false); // <- stdout come out just for failed tests
+}
+```
+
+```
+running 2 tests
+test should_fail::case_1 ... FAILED
+test should_fail::case_2 ... FAILED
+
+failures:
+
+---- should_fail::case_1 stdout ----
+------------ TEST ARGUMENTS ------------
+number = 42
+name = "FortyTwo"
+tuple = ("minus twelve", -12)
+-------------- TEST START --------------
+thread 'should_fail::case_1' panicked at 'assertion failed: false', src/main.rs:64:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
+
+---- should_fail::case_2 stdout ----
+------------ TEST ARGUMENTS ------------
+number = 24
+name = "TwentyFour"
+tuple = ("minus twentyfour", -24)
+-------------- TEST START --------------
+thread 'should_fail::case_2' panicked at 'assertion failed: false', src/main.rs:64:5
+
+
+failures:
+    should_fail::case_1
+    should_fail::case_2
+
+test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+In case one or more variables don't implement the `Debug` trait, an error
+is raised, but it's also possible to exclude a variable using the
+`#[notrace]` argument attribute.
+
+You can learn more on [Docs][docs-link] and find more examples in 
+[`tests/resources`](tests/resources) directory.
+
+## Changelog
+
+See [CHANGELOG.md](CHANGELOG.md)
+
+## License
+
+Licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+[license-apache-link])
+
+* MIT license [LICENSE-MIT](LICENSE-MIT) or [license-MIT-link]
+at your option.
+
+[//]: # (links)
+
+[crate-image]: https://img.shields.io/crates/v/rstest.svg
+[crate-link]: https://crates.io/crates/rstest
+[docs-image]: https://docs.rs/rstest/badge.svg
+[docs-link]: https://docs.rs/rstest/
+[test-action-image]: https://github.com/la10736/rstest/workflows/Test/badge.svg
+[test-action-link]: https://github.com/la10736/rstest/actions?query=workflow:Test
+[license-apache-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
+[license-mit-image]: https://img.shields.io/badge/license-MIT-blue.svg
+[license-apache-link]: http://www.apache.org/licenses/LICENSE-2.0
+[license-MIT-link]: http://opensource.org/licenses/MIT
+[reuse-crate-link]: https://crates.io/crates/rstest_reuse
diff --git a/third_party/rust/rstest/v0_12/crate/build.rs b/third_party/rust/rstest/v0_12/crate/build.rs
new file mode 100644
index 0000000..580edcb5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/build.rs
@@ -0,0 +1,30 @@
+use rustc_version::{version, version_meta, Channel};
+
+fn allow_features() -> Option<Vec<String>> {
+    std::env::var("CARGO_ENCODED_RUSTFLAGS").ok().map(|args| {
+        args.split('\u{001f}')
+            .filter(|arg| arg.starts_with("-Zallow-features="))
+            .map(|arg| arg.split('=').nth(1).unwrap())
+            .flat_map(|features| features.split(','))
+            .map(|f| f.to_owned())
+            .collect()
+    })
+}
+
+fn can_enable_proc_macro_diagnostic() -> bool {
+    allow_features()
+        .map(|f| f.iter().any(|f| dbg!(f) == "proc_macro_diagnostic"))
+        .unwrap_or(true)
+}
+
+fn main() {
+    let ver = version().unwrap();
+    assert!(ver.major >= 1);
+
+    match version_meta().unwrap().channel {
+        Channel::Nightly | Channel::Dev if can_enable_proc_macro_diagnostic() => {
+            println!("cargo:rustc-cfg=use_proc_macro_diagnostic");
+        }
+        _ => {}
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/checkoutlist.md b/third_party/rust/rstest/v0_12/crate/checkoutlist.md
new file mode 100644
index 0000000..716030b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/checkoutlist.md
@@ -0,0 +1,22 @@
+# TODO list
+
+- [ ] Update rustup
+- [ ] Update dependency `cargo upgrade`
+- [ ] Run `cargo clippy`
+- [ ] Run all test
+  - [ ] Stable: `RSTEST_TEST_CHANNEL=stable; cargo +${RSTEST_TEST_CHANNEL} test`
+  - [ ] Beta: `RSTEST_TEST_CHANNEL=beta; cargo +${RSTEST_TEST_CHANNEL} test`
+  - [ ] Nightly: `RSTEST_TEST_CHANNEL=nightly; cargo +${RSTEST_TEST_CHANNEL} test`
+- [ ] Check Cargo.toml version
+- [ ] Create docs and checks links
+- [ ] Check CHANGELOG: **RELEASE DATE** and remove empty blocks
+- [ ] Check README
+- [ ] Create tag (Use github release)
+- [ ] prepare deploy `cargo publish --dry-run`
+- [ ] deploy `cargo publish`
+- [ ] Change next version
+  - [ ] `Cargo.toml`
+  - [ ] `README.md`
+  - [ ] `CHANGELOG.md`
+- [ ] Change dependency (inner `rstest` and `rstest_ruse`)
+- [ ] Prepare next changelog
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/COPYRIGHT.txt b/third_party/rust/rstest/v0_12/crate/docs/head/COPYRIGHT.txt
new file mode 100644
index 0000000..16d7903
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/COPYRIGHT.txt
@@ -0,0 +1,46 @@
+These documentation pages include resources by third parties. This copyright
+file applies only to those resources. The following third party resources are
+included, and carry their own copyright notices and license terms:
+
+* Fira Sans (FiraSans-Regular.woff, FiraSans-Medium.woff):
+
+    Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
+    with Reserved Font Name Fira Sans.
+
+    Copyright (c) 2014, Telefonica S.A.
+
+    Licensed under the SIL Open Font License, Version 1.1.
+    See FiraSans-LICENSE.txt.
+
+* rustdoc.css, main.js, and playpen.js:
+
+    Copyright 2015 The Rust Developers.
+    Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or
+    the MIT license (LICENSE-MIT.txt) at your option.
+
+* normalize.css:
+
+    Copyright (c) Nicolas Gallagher and Jonathan Neal.
+    Licensed under the MIT license (see LICENSE-MIT.txt).
+
+* Source Code Pro (SourceCodePro-Regular.ttf.woff,
+    SourceCodePro-Semibold.ttf.woff, SourceCodePro-It.ttf.woff):
+
+    Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
+    with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
+    of Adobe Systems Incorporated in the United States and/or other countries.
+
+    Licensed under the SIL Open Font License, Version 1.1.
+    See SourceCodePro-LICENSE.txt.
+
+* Source Serif 4 (SourceSerif4-Regular.ttf.woff, SourceSerif4-Bold.ttf.woff,
+    SourceSerif4-It.ttf.woff):
+
+    Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
+    'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
+    States and/or other countries.
+
+    Licensed under the SIL Open Font License, Version 1.1.
+    See SourceSerif4-LICENSE.md.
+
+This copyright file is intended to be distributed with rustdoc output.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-LICENSE.txt b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-LICENSE.txt
new file mode 100644
index 0000000..d444ea9
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-LICENSE.txt
@@ -0,0 +1,94 @@
+Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
+with Reserved Font Name < Fira >, 
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Medium.woff b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Medium.woff
new file mode 100644
index 0000000..7d742c5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Medium.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Regular.woff b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Regular.woff
new file mode 100644
index 0000000..d8e0363
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/FiraSans-Regular.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-APACHE.txt b/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-APACHE.txt
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-APACHE.txt
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-MIT.txt b/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-MIT.txt
new file mode 100644
index 0000000..31aa7938
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/LICENSE-MIT.txt
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-LICENSE.txt b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-LICENSE.txt
new file mode 100644
index 0000000..0754257
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-LICENSE.txt
@@ -0,0 +1,93 @@
+Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Regular.woff b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Regular.woff
new file mode 100644
index 0000000..5576670
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Regular.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Semibold.woff b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Semibold.woff
new file mode 100644
index 0000000..ca972a1
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceCodePro-Semibold.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Bold.ttf.woff b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Bold.ttf.woff
new file mode 100644
index 0000000..ca254318
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Bold.ttf.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-It.ttf.woff b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-It.ttf.woff
new file mode 100644
index 0000000..a287bbe6
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-It.ttf.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-LICENSE.md b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-LICENSE.md
new file mode 100644
index 0000000..22cb755
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-LICENSE.md
@@ -0,0 +1,93 @@
+Copyright 2014-2018 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Regular.ttf.woff b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Regular.ttf.woff
new file mode 100644
index 0000000..a3d55cf
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/SourceSerifPro-Regular.ttf.woff
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/aliases.js b/third_party/rust/rstest/v0_12/crate/docs/head/aliases.js
new file mode 100644
index 0000000..095aa54
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/aliases.js
@@ -0,0 +1,2 @@
+var ALIASES = {};
+ALIASES["rstest"] = {};
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/brush.svg b/third_party/rust/rstest/v0_12/crate/docs/head/brush.svg
new file mode 100644
index 0000000..ea266e85
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/brush.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1792" height="1792" viewBox="0 0 1792 1792"><path d="M1615 0q70 0 122.5 46.5t52.5 116.5q0 63-45 151-332 629-465 752-97 91-218 91-126 0-216.5-92.5t-90.5-219.5q0-128 92-212l638-579q59-54 130-54zm-909 1034q39 76 106.5 130t150.5 76l1 71q4 213-129.5 347t-348.5 134q-123 0-218-46.5t-152.5-127.5-86.5-183-29-220q7 5 41 30t62 44.5 59 36.5 46 17q41 0 55-37 25-66 57.5-112.5t69.5-76 88-47.5 103-25.5 125-10.5z"/></svg>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/dark.css b/third_party/rust/rstest/v0_12/crate/docs/head/dark.css
new file mode 100644
index 0000000..d7ddae5a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/dark.css
@@ -0,0 +1 @@
+body{background-color:#353535;color:#ddd;}h1,h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod){color:#ddd;}h1.fqn{border-bottom-color:#d2d2d2;}h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod){border-bottom-color:#d2d2d2;}.in-band{background-color:#353535;}.invisible{background:rgba(0,0,0,0);}.docblock code,.docblock-short code{background-color:#2A2A2A;}pre,.rustdoc.source .example-wrap{background-color:#2A2A2A;}.sidebar{background-color:#505050;}.logo-container.rust-logo>img{filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff)}*{scrollbar-color:rgb(64,65,67) #717171;}.sidebar{scrollbar-color:rgba(32,34,37,.6) transparent;}::-webkit-scrollbar-track{background-color:#717171;}::-webkit-scrollbar-thumb{background-color:rgba(32,34,37,.6);}.sidebar::-webkit-scrollbar-track{background-color:#717171;}.sidebar::-webkit-scrollbar-thumb{background-color:rgba(32,34,37,.6);}.sidebar .current{background-color:#333;}.source .sidebar{background-color:#353535;}.sidebar .location{border-color:#fff;background:#575757;color:#DDD;}.sidebar .version{border-bottom-color:#DDD;}.sidebar-title{border-top-color:#777;border-bottom-color:#777;}.block a:hover{background:#444;}.line-numbers span{color:#3B91E2;}.line-numbers .line-highlighted{background-color:#0a042f !important;}.docblock h1,.docblock h2,.docblock h3,.docblock h4,.docblock h5{border-bottom-color:#DDD;}.docblock table,.docblock table td,.docblock table th{border-color:#ddd;}.content .method .where,.content .fn .where,.content .where.fmt-newline{color:#ddd;}.content .highlighted{color:#eee !important;background-color:#616161;}.content .highlighted a,.content .highlighted span{color:#eee !important;}.content .highlighted.trait{background-color:#013191;}.content .highlighted.traitalias{background-color:#013191;}.content .highlighted.mod,.content .highlighted.externcrate{background-color:#afc6e4;}.content .highlighted.mod{background-color:#803a1b;}.content .highlighted.externcrate{background-color:#396bac;}.content .highlighted.enum{background-color:#5b4e68;}.content .highlighted.struct{background-color:#194e9f;}.content .highlighted.union{background-color:#b7bd49;}.content .highlighted.fn,.content .highlighted.method,.content .highlighted.tymethod{background-color:#4950ed;}.content .highlighted.type{background-color:#38902c;}.content .highlighted.foreigntype{background-color:#b200d6;}.content .highlighted.attr,.content .highlighted.derive,.content .highlighted.macro{background-color:#217d1c;}.content .highlighted.constant,.content .highlighted.static{background-color:#0063cc;}.content .highlighted.primitive{background-color:#00708a;}.content .highlighted.keyword{background-color:#884719;}.content .item-info::before{color:#ccc;}.content span.enum,.content a.enum,.block a.current.enum{color:#82b089;}.content span.struct,.content a.struct,.block a.current.struct{color:#2dbfb8;}.content span.type,.content a.type,.block a.current.type{color:#ff7f00;}.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype{color:#dd7de8;}.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro{color:#09bd00;}.content span.union,.content a.union,.block a.current.union{color:#a6ae37;}.content span.constant,.content a.constant,.block a.current.constant,.content span.static,.content a.static,.block a.current.static{color:#82a5c9;}.content span.primitive,.content a.primitive,.block a.current.primitive{color:#43aec7;}.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod{color:#bda000;}.content span.trait,.content a.trait,.block a.current.trait{color:#b78cf2;}.content span.traitalias,.content a.traitalias,.block a.current.traitalias{color:#b397da;}.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,.content .fnname{color:#2BAB63;}.content span.keyword,.content a.keyword,.block a.current.keyword{color:#de5249;}pre.rust .comment{color:#8d8d8b;}pre.rust .doccomment{color:#8ca375;}nav:not(.sidebar){border-bottom-color:#4e4e4e;}nav.main .current{border-top-color:#eee;border-bottom-color:#eee;}nav.main .separator{border-color:#eee;}a{color:#ddd;}.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow),.docblock-short a:not(.srclink):not(.test-arrow),.item-info a,#help a{color:#D2991D;}a.test-arrow{color:#dedede;}details.rustdoc-toggle>summary.hideme>span,details.rustdoc-toggle>summary::before,details.undocumented>summary::before{color:#999;}#crate-search{color:#111;background-color:#f0f0f0;border-color:#000;box-shadow:0 0 0 1px #000,0 0 0 2px transparent;}.search-input{color:#111;background-color:#f0f0f0;box-shadow:0 0 0 1px #000,0 0 0 2px transparent;}.search-input:focus{border-color:#008dfd;}.search-focus:disabled{background-color:#c5c4c4;}#crate-search+.search-input:focus{box-shadow:0 0 8px 4px #078dd8;}.module-item .stab,.import-item .stab{color:#ddd;}.stab.unstable{background:#FFF5D6;border-color:#FFC600;color:#2f2f2f;}.stab.deprecated{background:#ffc4c4;border-color:#db7b7b;color:#2f2f2f;}.stab.portability{background:#F3DFFF;border-color:#b07bdb;color:#2f2f2f;}.stab.portability>code{background:none;}#help>div{background:#4d4d4d;border-color:#bfbfbf;}#help>div>span{border-bottom-color:#bfbfbf;}#help dt{border-color:#bfbfbf;background:rgba(0,0,0,0);}.since{color:grey;}tr.result span.primitive::after,tr.result span.keyword::after{color:#ddd;}.line-numbers :target{background-color:transparent;}pre.rust .kw{color:#ab8ac1;}pre.rust .kw-2,pre.rust .prelude-ty{color:#769acb;}pre.rust .number,pre.rust .string{color:#83a300;}pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute,pre.rust .attribute .ident{color:#ee6868;}pre.rust .macro,pre.rust .macro-nonterminal{color:#3E999F;}pre.rust .lifetime{color:#d97f26;}pre.rust .question-mark{color:#ff9011;}.example-wrap>pre.line-number{border-color:#4a4949;}a.test-arrow{background-color:rgba(78,139,202,0.2);}a.test-arrow:hover{background-color:#4e8bca;}.toggle-label,.code-attribute{color:#999;}:target>code,:target>.in-band{background-color:#494a3d;border-right:3px solid #bb7410;}pre.compile_fail{border-left:2px solid rgba(255,0,0,.8);}pre.compile_fail:hover,.information:hover+pre.compile_fail{border-left:2px solid #f00;}pre.should_panic{border-left:2px solid rgba(255,0,0,.8);}pre.should_panic:hover,.information:hover+pre.should_panic{border-left:2px solid #f00;}pre.ignore{border-left:2px solid rgba(255,142,0,.6);}pre.ignore:hover,.information:hover+pre.ignore{border-left:2px solid #ff9200;}.tooltip.compile_fail{color:rgba(255,0,0,.8);}.information>.compile_fail:hover{color:#f00;}.tooltip.should_panic{color:rgba(255,0,0,.8);}.information>.should_panic:hover{color:#f00;}.tooltip.ignore{color:rgba(255,142,0,.6);}.information>.ignore:hover{color:#ff9200;}.search-failed a{color:#0089ff;}.tooltip::after{background-color:#000;color:#fff;border-color:#000;}.tooltip::before{border-color:transparent black transparent transparent;}.notable-traits-tooltiptext{background-color:#111;border-color:#777;}#titles>button:not(.selected){background-color:#252525;border-top-color:#252525;}#titles>button:hover,#titles>button.selected{border-top-color:#0089ff;background-color:#353535;}#titles>button>div.count{color:#888;}@media (max-width:700px){.sidebar-menu{background-color:#505050;border-bottom-color:#e0e0e0;border-right-color:#e0e0e0;}.sidebar-elems{background-color:#505050;border-right-color:#000;}#sidebar-filler{background-color:#505050;border-bottom-color:#e0e0e0;}}kbd{color:#000;background-color:#fafbfc;border-color:#d1d5da;border-bottom-color:#c6cbd1;box-shadow-color:#c6cbd1;}#theme-picker,#settings-menu,#help-button,#copy-path{border-color:#e0e0e0;background:#f0f0f0;color:#000;}#theme-picker:hover,#theme-picker:focus,#settings-menu:hover,#settings-menu:focus,#help-button:hover,#help-button:focus,#copy-path:hover,#copy-path:focus{border-color:#ffb900;}#theme-choices{border-color:#e0e0e0;background-color:#353535;}#theme-choices>button:not(:first-child){border-top-color:#e0e0e0;}#theme-choices>button:hover,#theme-choices>button:focus{background-color:#4e4e4e;}@media (max-width:700px){#theme-picker{background:#f0f0f0;}}#all-types{background-color:#505050;}#all-types:hover{background-color:#606060;}.search-results td span.alias{color:#fff;}.search-results td span.grey{color:#ccc;}#sidebar-toggle{background-color:#565656;}#sidebar-toggle:hover{background-color:#676767;}#source-sidebar{background-color:#565656;}#source-sidebar>.title{border-bottom-color:#ccc;}div.files>a:hover,div.name:hover{background-color:#444;}div.files>.selected{background-color:#333;}.setting-line>.title{border-bottom-color:#ddd;}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/down-arrow.svg b/third_party/rust/rstest/v0_12/crate/docs/head/down-arrow.svg
new file mode 100644
index 0000000..35437e7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/down-arrow.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="128" height="128" enable-background="new 0 0 128 128" version="1.1" viewBox="-30 -20 176 176" xml:space="preserve"><g><line x1="111" x2="64" y1="40.5" y2="87.499" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/><line x1="64" x2="17" y1="87.499" y2="40.5" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/></g></svg>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/favicon.ico b/third_party/rust/rstest/v0_12/crate/docs/head/favicon.ico
new file mode 100644
index 0000000..b8ad2376
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/favicon.ico
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/light.css b/third_party/rust/rstest/v0_12/crate/docs/head/light.css
new file mode 100644
index 0000000..8682f22
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/light.css
@@ -0,0 +1 @@
+ body{background-color:white;color:black;}h1,h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod){color:black;}h1.fqn{border-bottom-color:#D5D5D5;}h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod){border-bottom-color:#DDDDDD;}.in-band{background-color:white;}.invisible{background:rgba(0,0,0,0);}.docblock code,.docblock-short code{background-color:#F5F5F5;}pre,.rustdoc.source .example-wrap{background-color:#F5F5F5;}.sidebar{background-color:#F1F1F1;}*{scrollbar-color:rgba(36,37,39,0.6) #e6e6e6;}.sidebar{scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;}.logo-container.rust-logo>img{}::-webkit-scrollbar-track{background-color:#ecebeb;}::-webkit-scrollbar-thumb{background-color:rgba(36,37,39,0.6);}.sidebar::-webkit-scrollbar-track{background-color:#dcdcdc;}.sidebar::-webkit-scrollbar-thumb{background-color:rgba(36,37,39,0.6);}.sidebar .current{background-color:#fff;}.source .sidebar{background-color:#fff;}.sidebar .location{border-color:#000;background-color:#fff;color:#333;}.sidebar .version{border-bottom-color:#DDD;}.sidebar-title{border-top-color:#777;border-bottom-color:#777;}.block a:hover{background:#F5F5F5;}.line-numbers span{color:#c67e2d;}.line-numbers .line-highlighted{background-color:#f6fdb0 !important;}.docblock h1,.docblock h2,.docblock h3,.docblock h4,.docblock h5{border-bottom-color:#ddd;}.docblock table,.docblock table td,.docblock table th{border-color:#ddd;}.content .method .where,.content .fn .where,.content .where.fmt-newline{color:#4E4C4C;}.content .highlighted{color:#000 !important;background-color:#ccc;}.content .highlighted a,.content .highlighted span{color:#000 !important;}.content .highlighted.trait{background-color:#c7b6ff;}.content .highlighted.traitalias{background-color:#c7b6ff;}.content .highlighted.mod,.content .highlighted.externcrate{background-color:#afc6e4;}.content .highlighted.enum{background-color:#b4d1b9;}.content .highlighted.struct{background-color:#e7b1a0;}.content .highlighted.union{background-color:#b7bd49;}.content .highlighted.fn,.content .highlighted.method,.content .highlighted.tymethod{background-color:#c6afb3;}.content .highlighted.type{background-color:#ffc891;}.content .highlighted.foreigntype{background-color:#f5c4ff;}.content .highlighted.attr,.content .highlighted.derive,.content .highlighted.macro{background-color:#8ce488;}.content .highlighted.constant,.content .highlighted.static{background-color:#c3e0ff;}.content .highlighted.primitive{background-color:#9aecff;}.content .highlighted.keyword{background-color:#f99650;}.content .item-info::before{color:#ccc;}.content span.enum,.content a.enum,.block a.current.enum{color:#508157;}.content span.struct,.content a.struct,.block a.current.struct{color:#ad448e;}.content span.type,.content a.type,.block a.current.type{color:#ba5d00;}.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype{color:#cd00e2;}.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro{color:#068000;}.content span.union,.content a.union,.block a.current.union{color:#767b27;}.content span.constant,.content a.constant,.block a.current.constant,.content span.static,.content a.static,.block a.current.static{color:#546e8a;}.content span.primitive,.content a.primitive,.block a.current.primitive{color:#2c8093;}.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod{color:#4d76ae;}.content span.trait,.content a.trait,.block a.current.trait{color:#7c5af3;}.content span.traitalias,.content a.traitalias,.block a.current.traitalias{color:#6841f1;}.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,.content .fnname{color:#9a6e31;}.content span.keyword,.content a.keyword,.block a.current.keyword{color:#de5249;}pre.rust .comment{color:#8E908C;}pre.rust .doccomment{color:#4D4D4C;}nav:not(.sidebar){border-bottom-color:#e0e0e0;}nav.main .current{border-top-color:#000;border-bottom-color:#000;}nav.main .separator{border:1px solid #000;}a{color:#000;}.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow),.docblock-short a:not(.srclink):not(.test-arrow),.item-info a,#help a{color:#3873AD;}a.test-arrow{color:#f5f5f5;}details.rustdoc-toggle>summary.hideme>span,details.rustdoc-toggle>summary::before,details.undocumented>summary::before{color:#999;}#crate-search{color:#555;background-color:white;border-color:#e0e0e0;box-shadow:0 0 0 1px #e0e0e0,0 0 0 2px transparent;}.search-input{color:#555;background-color:white;box-shadow:0 0 0 1px #e0e0e0,0 0 0 2px transparent;}.search-input:focus{border-color:#66afe9;}.search-focus:disabled{background-color:#e6e6e6;}#crate-search+.search-input:focus{box-shadow:0 0 8px #078dd8;}.module-item .stab,.import-item .stab{color:#000;}.stab.unstable{background:#FFF5D6;border-color:#FFC600;}.stab.deprecated{background:#ffc4c4;border-color:#db7b7b;}.stab.portability{background:#F3DFFF;border-color:#b07bdb;}.stab.portability>code{background:none;}#help>div{background:#e9e9e9;border-color:#bfbfbf;}#help>div>span{border-bottom-color:#bfbfbf;}.since{color:grey;}tr.result span.primitive::after,tr.result span.keyword::after{color:black;}.line-numbers :target{background-color:transparent;}pre.rust .kw{color:#8959A8;}pre.rust .kw-2,pre.rust .prelude-ty{color:#4271AE;}pre.rust .number,pre.rust .string{color:#718C00;}pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute,pre.rust .attribute .ident{color:#C82829;}pre.rust .macro,pre.rust .macro-nonterminal{color:#3E999F;}pre.rust .lifetime{color:#B76514;}pre.rust .question-mark{color:#ff9011;}.example-wrap>pre.line-number{border-color:#c7c7c7;}a.test-arrow{background-color:rgba(78,139,202,0.2);}a.test-arrow:hover{background-color:#4e8bca;}.toggle-label,.code-attribute{color:#999;}:target>code,:target>.in-band{background:#FDFFD3;border-right:3px solid #ffb44c;}pre.compile_fail{border-left:2px solid rgba(255,0,0,.5);}pre.compile_fail:hover,.information:hover+pre.compile_fail{border-left:2px solid #f00;}pre.should_panic{border-left:2px solid rgba(255,0,0,.5);}pre.should_panic:hover,.information:hover+pre.should_panic{border-left:2px solid #f00;}pre.ignore{border-left:2px solid rgba(255,142,0,.6);}pre.ignore:hover,.information:hover+pre.ignore{border-left:2px solid #ff9200;}.tooltip.compile_fail{color:rgba(255,0,0,.5);}.information>.compile_fail:hover{color:#f00;}.tooltip.should_panic{color:rgba(255,0,0,.5);}.information>.should_panic:hover{color:#f00;}.tooltip.ignore{color:rgba(255,142,0,.6);}.information>.ignore:hover{color:#ff9200;}.search-failed a{color:#0089ff;}.tooltip::after{background-color:#000;color:#fff;}.tooltip::before{border-color:transparent black transparent transparent;}.notable-traits-tooltiptext{background-color:#eee;border-color:#999;}#titles>button:not(.selected){background-color:#e6e6e6;border-top-color:#e6e6e6;}#titles>button:hover,#titles>button.selected{background-color:#ffffff;border-top-color:#0089ff;}#titles>button>div.count{color:#888;}@media (max-width:700px){.sidebar-menu{background-color:#F1F1F1;border-bottom-color:#e0e0e0;border-right-color:#e0e0e0;}.sidebar-elems{background-color:#F1F1F1;border-right-color:#000;}#sidebar-filler{background-color:#F1F1F1;border-bottom-color:#e0e0e0;}}kbd{color:#000;background-color:#fafbfc;border-color:#d1d5da;border-bottom-color:#c6cbd1;box-shadow-color:#c6cbd1;}#theme-picker,#settings-menu,#help-button,#copy-path{border-color:#e0e0e0;background-color:#fff;}#theme-picker:hover,#theme-picker:focus,#settings-menu:hover,#settings-menu:focus,#help-button:hover,#help-button:focus,#copy-path:hover,#copy-path:focus{border-color:#717171;}#theme-choices{border-color:#ccc;background-color:#fff;}#theme-choices>button:not(:first-child){border-top-color:#e0e0e0;}#theme-choices>button:hover,#theme-choices>button:focus{background-color:#eee;}@media (max-width:700px){#theme-picker{background:#fff;}}#all-types{background-color:#fff;}#all-types:hover{background-color:#f9f9f9;}.search-results td span.alias{color:#000;}.search-results td span.grey{color:#999;}#sidebar-toggle{background-color:#F1F1F1;}#sidebar-toggle:hover{background-color:#E0E0E0;}#source-sidebar{background-color:#F1F1F1;}#source-sidebar>.title{border-bottom-color:#ccc;}div.files>a:hover,div.name:hover{background-color:#E0E0E0;}div.files>.selected{background-color:#fff;}.setting-line>.title{border-bottom-color:#D5D5D5;}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/main.js b/third_party/rust/rstest/v0_12/crate/docs/head/main.js
new file mode 100644
index 0000000..25e2397
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/main.js
@@ -0,0 +1,8 @@
+if(!String.prototype.startsWith){String.prototype.startsWith=function(searchString,position){position=position||0;return this.indexOf(searchString,position)===position}}if(!String.prototype.endsWith){String.prototype.endsWith=function(suffix,length){var l=length||this.length;return this.indexOf(suffix,l-suffix.length)!==-1}}if(!DOMTokenList.prototype.add){DOMTokenList.prototype.add=function(className){if(className&&!hasClass(this,className)){if(this.className&&this.className.length>0){this.className+=" "+className}else{this.className=className}}}}if(!DOMTokenList.prototype.remove){DOMTokenList.prototype.remove=function(className){if(className&&this.className){this.className=(" "+this.className+" ").replace(" "+className+" "," ").trim()}}}(function(){var rustdocVars=document.getElementById("rustdoc-vars");if(rustdocVars){window.rootPath=rustdocVars.attributes["data-root-path"].value;window.currentCrate=rustdocVars.attributes["data-current-crate"].value;window.searchJS=rustdocVars.attributes["data-search-js"].value;window.searchIndexJS=rustdocVars.attributes["data-search-index-js"].value}var sidebarVars=document.getElementById("sidebar-vars");if(sidebarVars){window.sidebarCurrent={name:sidebarVars.attributes["data-name"].value,ty:sidebarVars.attributes["data-ty"].value,relpath:sidebarVars.attributes["data-relpath"].value,}}}());function getVirtualKey(ev){if("key"in ev&&typeof ev.key!="undefined"){return ev.key}var c=ev.charCode||ev.keyCode;if(c==27){return"Escape"}return String.fromCharCode(c)}var THEME_PICKER_ELEMENT_ID="theme-picker";var THEMES_ELEMENT_ID="theme-choices";function getThemesElement(){return document.getElementById(THEMES_ELEMENT_ID)}function getThemePickerElement(){return document.getElementById(THEME_PICKER_ELEMENT_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function showThemeButtonState(){var themePicker=getThemePickerElement();var themeChoices=getThemesElement();themeChoices.style.display="block";themePicker.style.borderBottomRightRadius="0";themePicker.style.borderBottomLeftRadius="0"}function hideThemeButtonState(){var themePicker=getThemePickerElement();var themeChoices=getThemesElement();themeChoices.style.display="none";themePicker.style.borderBottomRightRadius="3px";themePicker.style.borderBottomLeftRadius="3px"}(function(){var themeChoices=getThemesElement();var themePicker=getThemePickerElement();var availableThemes=["ayu","dark","light"];function switchThemeButtonState(){if(themeChoices.style.display==="block"){hideThemeButtonState()}else{showThemeButtonState()}}function handleThemeButtonsBlur(e){var active=document.activeElement;var related=e.relatedTarget;if(active.id!==THEME_PICKER_ELEMENT_ID&&(!active.parentNode||active.parentNode.id!==THEMES_ELEMENT_ID)&&(!related||(related.id!==THEME_PICKER_ELEMENT_ID&&(!related.parentNode||related.parentNode.id!==THEMES_ELEMENT_ID)))){hideThemeButtonState()}}themePicker.onclick=switchThemeButtonState;themePicker.onblur=handleThemeButtonsBlur;availableThemes.forEach(function(item){var but=document.createElement("button");but.textContent=item;but.onclick=function(){switchTheme(window.currentTheme,window.mainTheme,item,true);useSystemTheme(false)};but.onblur=handleThemeButtonsBlur;themeChoices.appendChild(but)})}());(function(){"use strict";window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:function(){return document.getElementById("search")},title:null,titleBeforeSearch:document.title,timeout:null,currentTab:0,mouseMovedAfterSearch:true,clearInputTimeout:function(){if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},focus:function(){searchState.input.focus()},defocus:function(){searchState.input.blur()},showResults:function(search){if(search===null||typeof search==='undefined'){search=searchState.outputElement()}addClass(main,"hidden");removeClass(search,"hidden");searchState.mouseMovedAfterSearch=false;document.title=searchState.title},hideResults:function(search){if(search===null||typeof search==='undefined'){search=searchState.outputElement()}addClass(search,"hidden");removeClass(main,"hidden");document.title=searchState.titleBeforeSearch;if(searchState.browserSupportsHistoryApi()){history.replaceState("",window.currentCrate+" - Rust",getNakedUrl()+window.location.hash)}},getQueryStringParams:function(){var params={};window.location.search.substring(1).split("&").map(function(s){var pair=s.split("=");params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},putBackSearch:function(search_input){var search=searchState.outputElement();if(search_input.value!==""&&hasClass(search,"hidden")){searchState.showResults(search);if(searchState.browserSupportsHistoryApi()){var extra="?search="+encodeURIComponent(search_input.value);history.replaceState(search_input.value,"",getNakedUrl()+extra+window.location.hash)}document.title=searchState.title}},browserSupportsHistoryApi:function(){return window.history&&typeof window.history.pushState==="function"},setup:function(){var search_input=searchState.input;if(!searchState.input){return}function loadScript(url){var script=document.createElement('script');script.src=url;document.head.append(script)}var searchLoaded=false;function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(window.searchJS);loadScript(window.searchIndexJS)}}search_input.addEventListener("focus",function(){searchState.putBackSearch(this);search_input.origPlaceholder=searchState.input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});search_input.addEventListener("blur",function(){search_input.placeholder=searchState.input.origPlaceholder});document.addEventListener("mousemove",function(){searchState.mouseMovedAfterSearch=true});search_input.removeAttribute('disabled');searchState.addCrateDropdown(window.ALL_CRATES);var params=searchState.getQueryStringParams();if(params.search!==undefined){var search=searchState.outputElement();search.innerHTML="<h3 style=\"text-align: center;\">"+searchState.loadingText+"</h3>";searchState.showResults(search);loadSearch()}},addCrateDropdown:function(crates){var elem=document.getElementById("crate-search");if(!elem){return}var savedCrate=getSettingValue("saved-filter-crate");for(var i=0,len=crates.length;i<len;++i){var option=document.createElement("option");option.value=crates[i];option.innerText=crates[i];elem.appendChild(option);if(crates[i]===savedCrate){elem.value=savedCrate}}},};function getPageId(){if(window.location.hash){var tmp=window.location.hash.replace(/^#/,"");if(tmp.length>0){return tmp}}return null}function showSidebar(){var elems=document.getElementsByClassName("sidebar-elems")[0];if(elems){addClass(elems,"show-it")}var sidebar=document.getElementsByClassName("sidebar")[0];if(sidebar){addClass(sidebar,"mobile");var filler=document.getElementById("sidebar-filler");if(!filler){var div=document.createElement("div");div.id="sidebar-filler";sidebar.appendChild(div)}}}function hideSidebar(){var elems=document.getElementsByClassName("sidebar-elems")[0];if(elems){removeClass(elems,"show-it")}var sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"mobile");var filler=document.getElementById("sidebar-filler");if(filler){filler.remove()}document.getElementsByTagName("body")[0].style.marginTop=""}var toggleAllDocsId="toggle-all-docs";var main=document.getElementById("main");var savedHash="";function handleHashes(ev){var elem;var search=searchState.outputElement();if(ev!==null&&search&&!hasClass(search,"hidden")&&ev.newURL){searchState.hideResults(search);var hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(searchState.browserSupportsHistoryApi()){history.replaceState(hash,"",getNakedUrl()+window.location.search+"#"+hash)}elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}if(savedHash!==window.location.hash){savedHash=window.location.hash;if(savedHash.length===0){return}expandSection(savedHash.slice(1))}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function getHelpElement(build){if(build){buildHelperPopup()}return document.getElementById("help")}function displayHelp(display,ev,help){if(display){help=help?help:getHelpElement(true);if(hasClass(help,"hidden")){ev.preventDefault();removeClass(help,"hidden");addClass(document.body,"blur")}}else{help=help?help:getHelpElement(false);if(help&&!hasClass(help,"hidden")){ev.preventDefault();addClass(help,"hidden");removeClass(document.body,"blur")}}}function handleEscape(ev){var help=getHelpElement(false);var search=searchState.outputElement();if(!hasClass(help,"hidden")){displayHelp(false,ev,help)}else if(!hasClass(search,"hidden")){searchState.clearInputTimeout();ev.preventDefault();searchState.hideResults(search)}searchState.defocus();hideThemeButtonState()}var disableShortcuts=getSettingValue("disable-shortcuts")==="true";function handleShortcut(ev){if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":displayHelp(false,ev);ev.preventDefault();searchState.focus();break;case"+":case"-":ev.preventDefault();toggleAllDocs();break;case"?":displayHelp(true,ev);break;case"t":case"T":displayHelp(false,ev);ev.preventDefault();var themePicker=getThemePickerElement();themePicker.click();themePicker.focus();break;default:if(getThemePickerElement().parentNode.contains(ev.target)){handleThemeKeyDown(ev)}}}}function handleThemeKeyDown(ev){var active=document.activeElement;var themes=getThemesElement();switch(getVirtualKey(ev)){case"ArrowUp":ev.preventDefault();if(active.previousElementSibling&&ev.target.id!==THEME_PICKER_ELEMENT_ID){active.previousElementSibling.focus()}else{showThemeButtonState();themes.lastElementChild.focus()}break;case"ArrowDown":ev.preventDefault();if(active.nextElementSibling&&ev.target.id!==THEME_PICKER_ELEMENT_ID){active.nextElementSibling.focus()}else{showThemeButtonState();themes.firstElementChild.focus()}break;case"Enter":case"Return":case"Space":if(ev.target.id===THEME_PICKER_ELEMENT_ID&&themes.style.display==="none"){ev.preventDefault();showThemeButtonState();themes.firstElementChild.focus()}break;case"Home":ev.preventDefault();themes.firstElementChild.focus();break;case"End":ev.preventDefault();themes.lastElementChild.focus();break}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);(function(){var x=document.getElementsByClassName("version-selector");if(x.length>0){x[0].onchange=function(){var i,match,url=document.location.href,stripped="",len=window.rootPath.match(/\.\.\//g).length+1;for(i=0;i<len;++i){match=url.match(/\/[^/]*$/);if(i<len-1){stripped=match[0]+stripped}url=url.substring(0,url.length-match[0].length)}var selectedVersion=document.getElementsByClassName("version-selector")[0].value;url+="/"+selectedVersion+stripped;document.location.href=url}}}());function addSidebarCrates(crates){if(window.rootPath==="../"||window.rootPath==="./"){var sidebar=document.getElementsByClassName("sidebar-elems")[0];if(sidebar){var div=document.createElement("div");div.className="block crate";div.innerHTML="<h3>Crates</h3>";var ul=document.createElement("ul");div.appendChild(ul);for(var i=0;i<crates.length;++i){var klass="crate";if(window.rootPath!=="./"&&crates[i]===window.currentCrate){klass+=" current"}var link=document.createElement("a");link.href=window.rootPath+crates[i]+"/index.html";link.className=klass;link.textContent=crates[i];var li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(div)}}}window.initSidebarItems=function(items){var sidebar=document.getElementsByClassName("sidebar-elems")[0];var current=window.sidebarCurrent;function block(shortty,longty){var filtered=items[shortty];if(!filtered){return}var div=document.createElement("div");div.className="block "+shortty;var h3=document.createElement("h3");h3.textContent=longty;div.appendChild(h3);var ul=document.createElement("ul");for(var i=0,len=filtered.length;i<len;++i){var item=filtered[i];var name=item[0];var desc=item[1];var klass=shortty;if(name===current.name&&shortty===current.ty){klass+=" current"}var path;if(shortty==="mod"){path=name+"/index.html"}else{path=shortty+"."+name+".html"}var link=document.createElement("a");link.href=current.relpath+path;link.title=desc;link.className=klass;link.textContent=name;var li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}div.appendChild(ul);if(sidebar){sidebar.appendChild(div)}}block("primitive","Primitive Types");block("mod","Modules");block("macro","Macros");block("struct","Structs");block("enum","Enums");block("union","Unions");block("constant","Constants");block("static","Statics");block("trait","Traits");block("fn","Functions");block("type","Type Definitions");block("foreigntype","Foreign Types");block("keyword","Keywords");block("traitalias","Trait Aliases");addSidebarCrates(window.ALL_CRATES)};window.register_implementors=function(imp){var implementors=document.getElementById("implementors-list");var synthetic_implementors=document.getElementById("synthetic-implementors-list");if(synthetic_implementors){var inlined_types=new Set();onEachLazy(synthetic_implementors.getElementsByClassName("impl"),function(el){var aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(function(alias){inlined_types.add(alias)})})}var libs=Object.getOwnPropertyNames(imp);for(var i=0,llength=libs.length;i<llength;++i){if(libs[i]===window.currentCrate){continue}var structs=imp[libs[i]];struct_loop:for(var j=0,slength=structs.length;j<slength;++j){var struct=structs[j];var list=struct.synthetic?synthetic_implementors:implementors;if(struct.synthetic){for(var k=0,stlength=struct.types.length;k<stlength;k++){if(inlined_types.has(struct.types[k])){continue struct_loop}inlined_types.add(struct.types[k])}}var code=document.createElement("code");code.innerHTML=struct.text;onEachLazy(code.getElementsByTagName("a"),function(elem){var href=elem.getAttribute("href");if(href&&href.indexOf("http")!==0){elem.setAttribute("href",window.rootPath+href)}});var display=document.createElement("h3");addClass(display,"impl");display.innerHTML="<span class=\"in-band\"><table class=\"table-display\">"+"<tbody><tr><td><code>"+code.outerHTML+"</code></td><td></td></tr>"+"</tbody></table></span>";list.appendChild(display)}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}function labelForToggleButton(sectionIsCollapsed){if(sectionIsCollapsed){return"+"}return"\u2212"}function toggleAllDocs(){var innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}var sectionIsCollapsed=false;if(hasClass(innerToggle,"will-expand")){removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("rustdoc-toggle"),function(e){if(!hasClass(e,"type-contents-toggle")){e.open=true}});innerToggle.title="collapse all docs"}else{addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("rustdoc-toggle"),function(e){if(e.parentNode.id!=="main"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});sectionIsCollapsed=true;innerToggle.title="expand all docs"}innerToggle.children[0].innerText=labelForToggleButton(sectionIsCollapsed)}function collapseDocs(toggle,mode){if(!toggle||!toggle.parentNode){return}function adjustToggle(arg){return function(e){if(hasClass(e,"toggle-label")){if(arg){e.style.display="inline-block"}else{e.style.display="none"}}if(hasClass(e,"inner")){e.innerHTML=labelForToggleButton(arg)}}}function implHider(addOrRemove,fullHide){return function(n){var shouldHide=fullHide||hasClass(n,"method")||hasClass(n,"associatedconstant");if(shouldHide||hasClass(n,"type")){if(shouldHide){if(addOrRemove){addClass(n,"hidden-by-impl-hider")}else{removeClass(n,"hidden-by-impl-hider")}}var ns=n.nextElementSibling;while(ns&&(hasClass(ns,"docblock")||hasClass(ns,"item-info"))){if(addOrRemove){addClass(ns,"hidden-by-impl-hider")}else{removeClass(ns,"hidden-by-impl-hider")}ns=ns.nextElementSibling}}}}var relatedDoc;var action=mode;if(!hasClass(toggle.parentNode,"impl")){relatedDoc=toggle.parentNode.nextElementSibling;if(hasClass(relatedDoc,"item-info")){relatedDoc=relatedDoc.nextElementSibling}if(hasClass(relatedDoc,"docblock")){if(mode==="toggle"){if(hasClass(relatedDoc,"hidden-by-usual-hider")){action="show"}else{action="hide"}}if(action==="hide"){addClass(relatedDoc,"hidden-by-usual-hider");onEachLazy(toggle.childNodes,adjustToggle(true));addClass(toggle.parentNode,"collapsed")}else if(action==="show"){removeClass(relatedDoc,"hidden-by-usual-hider");removeClass(toggle.parentNode,"collapsed");onEachLazy(toggle.childNodes,adjustToggle(false))}}}else{var parentElem=toggle.parentNode;relatedDoc=parentElem;var docblock=relatedDoc.nextElementSibling;while(!hasClass(relatedDoc,"impl-items")){relatedDoc=relatedDoc.nextElementSibling}if(!relatedDoc&&!hasClass(docblock,"docblock")){return}if(mode==="toggle"){if(hasClass(relatedDoc,"fns-now-collapsed")||hasClass(docblock,"hidden-by-impl-hider")){action="show"}else{action="hide"}}var dontApplyBlockRule=toggle.parentNode.parentNode.id!=="main";if(action==="show"){removeClass(relatedDoc,"fns-now-collapsed");if(!hasClass(docblock,"item-info")){removeClass(docblock,"hidden-by-usual-hider")}onEachLazy(toggle.childNodes,adjustToggle(false,dontApplyBlockRule));onEachLazy(relatedDoc.childNodes,implHider(false,dontApplyBlockRule))}else if(action==="hide"){addClass(relatedDoc,"fns-now-collapsed");if(!hasClass(docblock,"item-info")){addClass(docblock,"hidden-by-usual-hider")}onEachLazy(toggle.childNodes,adjustToggle(true,dontApplyBlockRule));onEachLazy(relatedDoc.childNodes,implHider(true,dontApplyBlockRule))}}}function collapseNonInherent(e){var n=e.parentElement;if(n.id.match(/^impl(?:-\d+)?$/)===null){if(hasClass(n,"impl")){collapseDocs(e,"hide")}}}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}(function(){var toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}var hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";var hideImplementors=getSettingValue("auto-collapse-implementors")!=="false";var hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";var impl_list=document.getElementById("trait-implementations-list");if(impl_list!==null){onEachLazy(impl_list.getElementsByClassName("rustdoc-toggle"),function(e){collapseNonInherent(e)})}var blanket_list=document.getElementById("blanket-implementations-list");if(blanket_list!==null){onEachLazy(blanket_list.getElementsByClassName("rustdoc-toggle"),function(e){collapseNonInherent(e)})}if(hideMethodDocs){onEachLazy(document.getElementsByClassName("method"),function(e){var toggle=e.parentNode;if(toggle){toggle=toggle.parentNode}if(toggle&&toggle.tagName==="DETAILS"){toggle.open=false}})}onEachLazy(document.getElementsByTagName("details"),function(e){var showLargeItem=!hideLargeItemContents&&hasClass(e,"type-contents-toggle");var showImplementor=!hideImplementors&&hasClass(e,"implementors-toggle");if(showLargeItem||showImplementor){e.open=true}});var currentType=document.getElementsByClassName("type-decl")[0];if(currentType){currentType=currentType.getElementsByClassName("rust")[0];if(currentType){onEachLazy(currentType.classList,function(item){if(item!=="main"){return true}})}}var pageId=getPageId();if(pageId!==null){expandSection(pageId)}}());(function(){var lineNumbersFunc=function(){};if(getSettingValue("line-numbers")==="true"){lineNumbersFunc=function(x){var count=x.textContent.split("\n").length;var elems=[];for(var i=0;i<count;++i){elems.push(i+1)}var node=document.createElement("pre");addClass(node,"line-number");node.innerHTML=elems.join("\n");x.parentNode.insertBefore(node,x)}}onEachLazy(document.getElementsByClassName("rust-example-rendered"),function(e){if(hasClass(e,"compile_fail")){e.addEventListener("mouseover",function(){this.parentElement.previousElementSibling.childNodes[0].style.color="#f00"});e.addEventListener("mouseout",function(){this.parentElement.previousElementSibling.childNodes[0].style.color=""})}else if(hasClass(e,"ignore")){e.addEventListener("mouseover",function(){this.parentElement.previousElementSibling.childNodes[0].style.color="#ff9200"});e.addEventListener("mouseout",function(){this.parentElement.previousElementSibling.childNodes[0].style.color=""})}lineNumbersFunc(e)})}());function handleClick(id,f){var elem=document.getElementById(id);if(elem){elem.addEventListener("click",f)}}handleClick("help-button",function(ev){displayHelp(true,ev)});onEachLazy(document.getElementsByTagName("a"),function(el){if(el.hash){el.addEventListener("click",function(){expandSection(el.hash.slice(1))})}});onEachLazy(document.getElementsByClassName("notable-traits"),function(e){e.onclick=function(){this.getElementsByClassName('notable-traits-tooltiptext')[0].classList.toggle("force-tooltip")}});var sidebar_menu=document.getElementsByClassName("sidebar-menu")[0];if(sidebar_menu){sidebar_menu.onclick=function(){var sidebar=document.getElementsByClassName("sidebar")[0];if(hasClass(sidebar,"mobile")){hideSidebar()}else{showSidebar()}}}var buildHelperPopup=function(){var popup=document.createElement("aside");addClass(popup,"hidden");popup.id="help";popup.addEventListener("click",function(ev){if(ev.target===popup){displayHelp(false,ev)}});var book_info=document.createElement("span");book_info.innerHTML="You can find more information in \
+            <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";var container=document.createElement("div");var shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["T","Focus the theme picker menu"],["↑","Move up in search results"],["↓","Move down in search results"],["ctrl + ↑ / ↓","Switch result tab"],["&#9166;","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(function(x){return"<dt>"+x[0].split(" ").map(function(y,index){return(index&1)===0?"<kbd>"+y+"</kbd>":" "+y+" "}).join("")+"</dt><dd>"+x[1]+"</dd>"}).join("");var div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="<h2>Keyboard Shortcuts</h2><dl>"+shortcuts+"</dl></div>";var infos=["Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
+             restrict the search to a given item kind.","Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
+             <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \
+             and <code>const</code>.","Search functions by type signature (e.g., <code>vec -&gt; usize</code> or \
+             <code>* -&gt; vec</code>)","Search multiple things at once by splitting your query with comma (e.g., \
+             <code>str,u8</code> or <code>String,struct:Vec,test</code>)","You can look for items with an exact name by putting double quotes around \
+             your request: <code>\"string\"</code>","Look for items inside another one by searching for a path: <code>vec::Vec</code>",].map(function(x){return"<p>"+x+"</p>"}).join("");var div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="<h2>Search Tricks</h2>"+infos;container.appendChild(book_info);container.appendChild(div_shortcuts);container.appendChild(div_infos);popup.appendChild(container);insertAfter(popup,searchState.outputElement());buildHelperPopup=function(){}};onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){var reset_button_timeout=null;window.copy_path=function(but){var parent=but.parentElement;var path=[];onEach(parent.childNodes,function(child){if(child.tagName==='A'){path.push(child.textContent)}});var el=document.createElement('textarea');el.value='use '+path.join('::')+';';el.setAttribute('readonly','');el.style.position='absolute';el.style.left='-9999px';document.body.appendChild(el);el.select();document.execCommand('copy');document.body.removeChild(el);but.children[0].style.display='none';var tmp;if(but.childNodes.length<2){tmp=document.createTextNode('✓');but.appendChild(tmp)}else{onEachLazy(but.childNodes,function(e){if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent='✓'}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent='';reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}())
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/normalize.css b/third_party/rust/rstest/v0_12/crate/docs/head/normalize.css
new file mode 100644
index 0000000..6d692b5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/normalize.css
@@ -0,0 +1,2 @@
+ /*! normalize.css v3.0.0 | MIT License | git.io/normalize */
+html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/noscript.css b/third_party/rust/rstest/v0_12/crate/docs/head/noscript.css
new file mode 100644
index 0000000..aea68efb
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/noscript.css
@@ -0,0 +1 @@
+ #main .attributes{margin-left:0 !important;}#copy-path{display:none;}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/all.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/all.html
new file mode 100644
index 0000000..8d883ec
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/all.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><meta name="keywords" content="rust, rustlang, rust-lang"><title>List of all items in this crate</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../ayu.css" disabled ><script id="default-settings"></script><script src="../storage.js"></script><script src="../crates.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc mod"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../rstest/index.html'><div class='logo-container rust-logo'><img src='../rust-logo.png' alt='logo'></div></a><p class="location">Crate rstest</p><div class="block version"><p>Version 0.11.0</p></div><a id="all-types" href="index.html"><p>Back to index</p></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class="fqn"><span class="in-band">List of all items</span><span class="out-of-band"><span id="render-detail"><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class="inner">&#x2212;</span>]</a></span>
+                 </span>
+             </h1><h3 id="Attribute Macros">Attribute Macros</h3><ul class="attributes docblock"><li><a href="attr.fixture.html">fixture</a></li><li><a href="attr.rstest.html">rstest</a></li></ul></section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../" data-current-crate="rstest" data-search-index-js="../search-index.js" data-search-js="../search.js"></div>
+    <script src="../main.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.fixture.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.fixture.html
new file mode 100644
index 0000000..daf9a81a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.fixture.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Define a fixture that you can use in all `rstest`’s test arguments. You should just mark your function as `#[fixture]` and then use it as a test’s argument. Fixture functions can also use other fixtures."><meta name="keywords" content="rust, rustlang, rust-lang, fixture"><title>fixture in rstest - Rust</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../ayu.css" disabled ><script id="default-settings"></script><script src="../storage.js"></script><script src="../crates.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc attr"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../rstest/index.html'><div class='logo-container rust-logo'><img src='../rust-logo.png' alt='logo'></div></a><div class="sidebar-elems"><p class="location"><a href="index.html">rstest</a></p><div id="sidebar-vars" data-name="fixture" data-ty="attr" data-relpath=""></div><script defer src="sidebar-items.js"></script></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class="fqn"><span class="in-band">Attribute Macro <a href="index.html">rstest</a>::<wbr><a class="attr" href="">fixture</a><button id="copy-path" onclick="copy_path(this)"><img src="../clipboard.svg" width="19" height="18" alt="Copy item import"></button></span><span class="out-of-band"><span id="render-detail"><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class="inner">&#x2212;</span>]</a></span><a class="srclink" href="../src/rstest/lib.rs.html#498-523" title="goto source code">[src]</a></span></h1><pre class="rust attr">#[fixture]</pre><details class="rustdoc-toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Define a fixture that you can use in all <code>rstest</code>’s test arguments. You should just mark your
+function as <code>#[fixture]</code> and then use it as a test’s argument. Fixture functions can also
+use other fixtures.</p>
+<p>Let’s see a trivial example:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">twenty_one</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">21</span> }
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">two</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">2</span> }
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>(<span class="ident">twenty_one</span>: <span class="ident">i32</span>, <span class="ident">two</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="ident">twenty_one</span> <span class="op">*</span> <span class="ident">two</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p>If the fixture function is an <a href="#async"><code>async</code> function</a> your fixture become an <code>async</code>
+fixture.</p>
+<h1 id="default-values" class="section-header"><a href="#default-values">Default values</a></h1>
+<p>If you need to define argument default value you can use <code>#[default(expression)]</code>
+argument’s attribute:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>(
+    <span class="attribute">#[<span class="ident">default</span>(<span class="number">21</span>)]</span>
+    <span class="ident">twenty_one</span>: <span class="ident">i32</span>,
+    <span class="attribute">#[<span class="ident">default</span>(<span class="number">1</span> <span class="op">+</span> <span class="number">1</span>)]</span>
+    <span class="ident">two</span>: <span class="ident">i32</span>
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="ident">twenty_one</span> <span class="op">*</span> <span class="ident">two</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p>The <code>expression</code> could be any valid rust expression, even an <code>async</code> block if you need.
+Moreover, if the type implements <code>FromStr</code> trait you can use a literal string to build it.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">db_connection</span>(
+    <span class="attribute">#[<span class="ident">default</span>(<span class="string">&quot;127.0.0.1:9000&quot;</span>)]</span>
+    <span class="ident">addr</span>: <span class="ident">SocketAddr</span>
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">DbConnection</span> {
+    <span class="comment">// create connection</span>
+}</pre></div>
+<h1 id="async" class="section-header"><a href="#async">Async</a></h1>
+<p>If you need you can write <code>async</code> fixtures to use in your <code>async</code> tests. Simply use <code>async</code>
+keyword for your function and the fixture become an <code>async</code> fixture.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">async_fixture</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">the_test</span>(<span class="attribute">#[<span class="ident">future</span>]</span> <span class="ident">async_fixture</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">async_fixture</span>.<span class="kw">await</span>)
+}</pre></div>
+<p>The <code>#[future]</code> argument attribute helps to remove the <code>impl Future&lt;Output = T&gt;</code> boilerplate.
+In this case the macro expands it in:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">async_fixture</span>: <span class="kw">impl</span> <span class="ident">std::future::Future</span><span class="op">&lt;</span><span class="ident">Output</span> <span class="op">=</span> <span class="ident">i32</span><span class="op">&gt;</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">async_fixture</span>.<span class="kw">await</span>)
+}</pre></div>
+<p>If you need, you can use <code>#[future]</code> attribute also with an implicit lifetime reference
+because the macro will replace the implicit lifetime with an explicit one.</p>
+<h1 id="rename" class="section-header"><a href="#rename">Rename</a></h1>
+<p>Sometimes you want to have long and descriptive name for your fixture but you prefer to use a much
+shorter name for argument that represent it in your fixture or test. You can rename the fixture
+using <code>#[from(short_name)]</code> attribute like following example:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">long_and_boring_descriptive_name</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="attribute">#[<span class="ident">from</span>(<span class="ident">long_and_boring_descriptive_name</span>)]</span> <span class="ident">short</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">short</span>)
+}</pre></div>
+<h1 id="partial-injection" class="section-header"><a href="#partial-injection">Partial Injection</a></h1>
+<p>You can also partialy inject fixture dependency using <code>#[with(v1, v2, ..)]</code> attribute:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">base</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">1</span> }
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">first</span>(<span class="ident">base</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">1</span> <span class="op">*</span> <span class="ident">base</span> }
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">second</span>(<span class="ident">base</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">2</span> <span class="op">*</span> <span class="ident">base</span> }
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>(<span class="ident">first</span>: <span class="ident">i32</span>, <span class="attribute">#[<span class="ident">with</span>(<span class="number">3</span>)]</span> <span class="ident">second</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="ident">first</span> <span class="op">*</span> <span class="ident">second</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="op">-</span><span class="number">6</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p>Note that injected value can be an arbitrary rust expression. <code>#[with(v1, ..., vn)]</code>
+attribute will inject <code>v1, ..., vn</code> expression as fixture arguments: all remaining arguments
+will be resolved as fixtures.</p>
+<p>Sometimes the return type cannot be infered so you must define it: For the few times you may
+need to do it, you can use the <code>#[default(type)]</code>, <code>#[partial_n(type)]</code> function attribute
+to define it:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">i</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">u32</span> {
+    <span class="number">42</span>
+}
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">j</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> {
+    <span class="op">-</span><span class="number">42</span>
+}
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="attribute">#[<span class="ident">default</span>(<span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">u32</span>, <span class="ident">i32</span>)<span class="op">&gt;</span>)]</span>
+<span class="attribute">#[<span class="ident">partial_1</span>(<span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">I</span>,<span class="ident">i32</span>)<span class="op">&gt;</span>)]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">fx</span><span class="op">&lt;</span><span class="ident">I</span>, <span class="ident">J</span><span class="op">&gt;</span>(<span class="ident">i</span>: <span class="ident">I</span>, <span class="ident">j</span>: <span class="ident">J</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">I</span>, <span class="ident">J</span>)<span class="op">&gt;</span> {
+    <span class="ident">std::iter::once</span>((<span class="ident">i</span>, <span class="ident">j</span>))
+}
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">resolve_by_default</span><span class="op">&lt;</span><span class="ident">I</span>: <span class="ident">Debug</span> <span class="op">+</span> <span class="ident">PartialEq</span><span class="op">&gt;</span>(<span class="kw-2">mut</span> <span class="ident">fx</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span><span class="ident">I</span><span class="op">&gt;</span>) {
+    <span class="macro">assert_eq!</span>((<span class="number">42</span>, <span class="op">-</span><span class="number">42</span>), <span class="ident">fx</span>.<span class="ident">next</span>().<span class="ident">unwrap</span>())
+}
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">resolve_partial</span><span class="op">&lt;</span><span class="ident">I</span>: <span class="ident">Debug</span> <span class="op">+</span> <span class="ident">PartialEq</span><span class="op">&gt;</span>(<span class="attribute">#[<span class="ident">with</span>(<span class="number">42.0</span>)]</span> <span class="kw-2">mut</span> <span class="ident">fx</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span><span class="ident">I</span><span class="op">&gt;</span>) {
+    <span class="macro">assert_eq!</span>((<span class="number">42.0</span>, <span class="op">-</span><span class="number">42</span>), <span class="ident">fx</span>.<span class="ident">next</span>().<span class="ident">unwrap</span>())
+}</pre></div>
+<p><code>partial_i</code> is the fixture used when you inject the first <code>i</code> arguments in test call.</p>
+<h1 id="old-compact-syntax" class="section-header"><a href="#old-compact-syntax">Old <em>compact</em> syntax</a></h1>
+<p>There is also a compact form for all previous features. This will mantained for a long time
+but for <code>fixture</code> I strongly recomand to migrate your code because you’ll pay a little
+verbosity but get back a more readable code.</p>
+<p>Follow the previous examples in old <em>compact</em> syntax.</p>
+<h2 id="default" class="section-header"><a href="#default">Default</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>(<span class="ident">twenty_one</span><span class="op">=</span><span class="number">21</span>, <span class="ident">two</span><span class="op">=</span><span class="number">2</span>)]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>(<span class="ident">twenty_one</span>: <span class="ident">i32</span>, <span class="ident">two</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="ident">twenty_one</span> <span class="op">*</span> <span class="ident">two</span> }</pre></div>
+<h2 id="rename-1" class="section-header"><a href="#rename-1">Rename</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">long_and_boring_descriptive_name</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>(<span class="ident">long_and_boring_descriptive_name</span> <span class="kw">as</span> <span class="ident">short</span>)]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">short</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">short</span>)
+}</pre></div>
+<h2 id="partial-injection-1" class="section-header"><a href="#partial-injection-1">Partial Injection</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>(<span class="ident">second</span>(<span class="op">-</span><span class="number">3</span>))]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>(<span class="ident">first</span>: <span class="ident">i32</span>, <span class="ident">second</span>: <span class="ident">i32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="ident">first</span> <span class="op">*</span> <span class="ident">second</span> }</pre></div>
+<h2 id="partial-type-injection" class="section-header"><a href="#partial-type-injection">Partial Type Injection</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>(<span class="ident">::default</span><span class="op">&lt;</span><span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">u32</span>, <span class="ident">i32</span>)<span class="op">&gt;</span><span class="op">&gt;</span><span class="ident">::partial_1</span><span class="op">&lt;</span><span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">I</span>,<span class="ident">i32</span>)<span class="op">&gt;</span><span class="op">&gt;</span>)]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">fx</span><span class="op">&lt;</span><span class="ident">I</span>, <span class="ident">J</span><span class="op">&gt;</span>(<span class="ident">i</span>: <span class="ident">I</span>, <span class="ident">j</span>: <span class="ident">J</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">I</span>, <span class="ident">J</span>)<span class="op">&gt;</span> {
+    <span class="ident">std::iter::once</span>((<span class="ident">i</span>, <span class="ident">j</span>))
+}</pre></div>
+</div></details></section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../" data-current-crate="rstest" data-search-index-js="../search-index.js" data-search-js="../search.js"></div>
+    <script src="../main.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest.html
new file mode 100644
index 0000000..599f29a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest.html
@@ -0,0 +1,494 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="The attribute that you should use for your tests. Your annotated function’s arguments can be injected with `[fixture]`s, provided by parametrized cases or by value lists."><meta name="keywords" content="rust, rustlang, rust-lang, rstest"><title>rstest in rstest - Rust</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../ayu.css" disabled ><script id="default-settings"></script><script src="../storage.js"></script><script src="../crates.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc attr"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../rstest/index.html'><div class='logo-container rust-logo'><img src='../rust-logo.png' alt='logo'></div></a><div class="sidebar-elems"><p class="location"><a href="index.html">rstest</a></p><div id="sidebar-vars" data-name="rstest" data-ty="attr" data-relpath=""></div><script defer src="sidebar-items.js"></script></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class="fqn"><span class="in-band">Attribute Macro <a href="index.html">rstest</a>::<wbr><a class="attr" href="">rstest</a><button id="copy-path" onclick="copy_path(this)"><img src="../clipboard.svg" width="19" height="18" alt="Copy item import"></button></span><span class="out-of-band"><span id="render-detail"><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class="inner">&#x2212;</span>]</a></span><a class="srclink" href="../src/rstest/lib.rs.html#1159-1190" title="goto source code">[src]</a></span></h1><pre class="rust attr">#[rstest]</pre><details class="rustdoc-toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>The attribute that you should use for your tests. Your
+annotated function’s arguments can be
+<a href="attr.rstest.html#injecting-fixtures">injected</a> with
+<a href="attr.fixture.html"><code>[fixture]</code></a>s, provided by
+<a href="attr.rstest.html#test-parametrized-cases">parametrized cases</a>
+or by <a href="attr.rstest.html#values-lists">value lists</a>.</p>
+<p><code>rstest</code> attribute can be applied to <em>any</em> function and you can costumize its
+parameters by using function and arguments attributes.</p>
+<p>Your test function can use generics, <code>impl</code> or <code>dyn</code> and like any kind of rust tests:</p>
+<ul>
+<li>return results</li>
+<li>marked by <code>#[should_panic]</code> attribute</li>
+</ul>
+<p>If the test function is an <a href="#async"><code>async</code> function</a> <code>rstest</code> will run all tests as <code>async</code>
+tests. You can use it just with <code>async-std</code> and you should include <code>attributes</code> in
+<code>async-std</code>’s features.</p>
+<p>In your test function you can:</p>
+<ul>
+<li><a href="#injecting-fixtures">injecting fixtures</a></li>
+<li>Generate <a href="#test-parametrized-cases">parametrized test cases</a></li>
+<li>Generate tests for each combination of <a href="#values-lists">value lists</a></li>
+</ul>
+<h2 id="injecting-fixtures" class="section-header"><a href="#injecting-fixtures">Injecting Fixtures</a></h2>
+<p>The simplest case is write a test that can be injected with
+<a href="attr.fixture.html"><code>[fixture]</code></a>s. You can just declare all used fixtures by passing
+them as a function’s arguments. This can help your test to be neat
+and make your dependecy clear.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p><a href="attr.rstest.html"><code>[rstest]</code></a> procedural macro will desugar it to something that isn’t
+so far from</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">test</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>() {
+    <span class="kw">let</span> <span class="ident">injected</span><span class="op">=</span><span class="ident">injected</span>();
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p>If you want to use long and descriptive names for your fixture but prefer to use
+shorter names inside your tests you use rename feature described in 
+<a href="attr.fixture.html#rename">fixture rename</a>:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">long_and_boring_descriptive_name</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="attribute">#[<span class="ident">from</span>(<span class="ident">long_and_boring_descriptive_name</span>)]</span> <span class="ident">short</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">short</span>)
+}</pre></div>
+<p>Sometimes is useful to have some parametes in your fixtures but your test would
+override the fixture’s default values in some cases. Like in
+<a href="attr.fixture.html#partial-injection">fixture partial injection</a> you use <code>#[with]</code>
+attribute to indicate some fixture’s arguments also in <code>rstest</code>.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">user</span>(
+    <span class="attribute">#[<span class="ident">default</span>(<span class="string">&quot;Alice&quot;</span>)]</span> <span class="ident">name</span>: <span class="kw">impl</span> <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span>,
+    <span class="attribute">#[<span class="ident">default</span>(<span class="number">22</span>)]</span> <span class="ident">age</span>: <span class="ident">u8</span>
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">User</span> { <span class="ident">User</span>(<span class="ident">name</span>.<span class="ident">as_ref</span>().<span class="ident">to_owned</span>(), <span class="ident">age</span>) }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">check_user</span>(<span class="attribute">#[<span class="ident">with</span>(<span class="string">&quot;Bob&quot;</span>)]</span> <span class="ident">user</span>: <span class="ident">User</span>) {
+    <span class="ident">assert_eq</span>(<span class="string">&quot;Bob&quot;</span>, <span class="ident">user</span>.<span class="ident">name</span>())
+}</pre></div>
+<h2 id="test-parametrized-cases" class="section-header"><a href="#test-parametrized-cases">Test Parametrized Cases</a></h2>
+<p>If you would execute your test for a set of input data cases
+you can define the arguments to use and the cases list. Let see
+the classical Fibonacci example. In this case we would give the
+<code>input</code> value and the <code>expected</code> result for a set of cases to test.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest::rstest</span>;
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">0</span>, <span class="number">0</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">1</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">3</span>, <span class="number">2</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">4</span>, <span class="number">3</span>)]</span>
+<span class="kw">fn</span> <span class="ident">fibonacci_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">input</span>: <span class="ident">u32</span>,<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">fibonacci</span>(<span class="ident">input</span>))
+}
+
+<span class="kw">fn</span> <span class="ident">fibonacci</span>(<span class="ident">input</span>: <span class="ident">u32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">u32</span> {
+    <span class="kw">match</span> <span class="ident">input</span> {
+        <span class="number">0</span> <span class="op">=</span><span class="op">&gt;</span> <span class="number">0</span>,
+        <span class="number">1</span> <span class="op">=</span><span class="op">&gt;</span> <span class="number">1</span>,
+        <span class="ident">n</span> <span class="op">=</span><span class="op">&gt;</span> <span class="ident">fibonacci</span>(<span class="ident">n</span> <span class="op">-</span> <span class="number">2</span>) <span class="op">+</span> <span class="ident">fibonacci</span>(<span class="ident">n</span> <span class="op">-</span> <span class="number">1</span>)
+    }
+}</pre></div>
+<p><code>rstest</code> will produce 5 indipendent tests and not just one that
+check every case. Every test can fail indipendently and <code>cargo test</code>
+will give follow output:</p>
+<pre><code class="language-text">running 5 tests
+test fibonacci_test::case_1 ... ok
+test fibonacci_test::case_2 ... ok
+test fibonacci_test::case_3 ... ok
+test fibonacci_test::case_4 ... ok
+test fibonacci_test::case_5 ... ok
+
+test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+</code></pre>
+<p>The cases input values can be arbitrary Rust expresions that return the
+argument type.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest::rstest</span>;
+  
+<span class="kw">fn</span> <span class="ident">sum</span>(<span class="ident">a</span>: <span class="ident">usize</span>, <span class="ident">b</span>: <span class="ident">usize</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">usize</span> { <span class="ident">a</span> <span class="op">+</span> <span class="ident">b</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;foo&quot;</span>, <span class="number">3</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="ident">String::from</span>(<span class="string">&quot;foo&quot;</span>), <span class="number">2</span> <span class="op">+</span> <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="macro">format!</span>(<span class="string">&quot;foo&quot;</span>), <span class="ident">sum</span>(<span class="number">2</span>, <span class="number">1</span>))]</span>
+<span class="kw">fn</span> <span class="ident">test_len</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">s</span>: <span class="kw">impl</span> <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span>,<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">len</span>: <span class="ident">usize</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">s</span>.<span class="ident">as_ref</span>().<span class="ident">len</span>(), <span class="ident">len</span>);
+}</pre></div>
+<h3 id="magic-conversion" class="section-header"><a href="#magic-conversion">Magic Conversion</a></h3>
+<p>You can use the magic conversion feature every time you would define a variable
+where its type define <code>FromStr</code> trait: test will parse the string to build the value.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;resources/empty&quot;</span>, <span class="number">0</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;resources/divine_commedy&quot;</span>, <span class="number">101.698</span>)]</span>
+<span class="kw">fn</span> <span class="ident">test_count_words</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">path</span>: <span class="ident">PathBuf</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">usize</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">count_words</span>(<span class="ident">path</span>))
+}</pre></div>
+<h3 id="optional-case-description" class="section-header"><a href="#optional-case-description">Optional case description</a></h3>
+<p>Optionally you can give a <em>description</em> to every case simple by follow <code>case</code>
+with <code>::my_case_description</code> where <code>my_case_description</code> should be a a valid
+Rust ident.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case::zero_base_case</span>(<span class="number">0</span>, <span class="number">0</span>)]</span>
+<span class="attribute">#[<span class="ident">case::one_base_case</span>(<span class="number">1</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">3</span>, <span class="number">2</span>)]</span>
+<span class="kw">fn</span> <span class="ident">fibonacci_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">input</span>: <span class="ident">u32</span>,<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">fibonacci</span>(<span class="ident">input</span>))
+}
+</pre></div>
+<p>Outuput will be</p>
+<pre><code class="language-text">running 4 tests
+test fibonacci_test::case_1_zero_base_case ... ok
+test fibonacci_test::case_2_one_base_case ... ok
+test fibonacci_test::case_3 ... ok
+test fibonacci_test::case_4 ... ok
+
+test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+</code></pre>
+<h3 id="use-specific-case-attributes" class="section-header"><a href="#use-specific-case-attributes">Use specific <code>case</code> attributes</a></h3>
+<p>Every function’s attributes that preceding a <code>#[case]</code> attribute will
+be used in this test case and all function’s attributes that follow the
+last <code>#[case]</code> attribute will mark all test cases.</p>
+<p>This feature can be use to mark just some cases as <code>should_panic</code>
+and choose to have a fine grain on expected panic messages.</p>
+<p>In follow example we run 3 tests where the first pass without any
+panic, in the second we catch a panic but we don’t care about the message
+and in the third one we also check the panic message.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest::rstest</span>;
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case::no_panic</span>(<span class="number">0</span>)]</span>
+<span class="attribute">#[<span class="ident">should_panic</span>]</span>
+<span class="attribute">#[<span class="ident">case::panic</span>(<span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">should_panic</span>(<span class="ident">expected</span><span class="op">=</span><span class="string">&quot;expected&quot;</span>)]</span>
+<span class="attribute">#[<span class="ident">case::panic_with_message</span>(<span class="number">2</span>)]</span>
+<span class="kw">fn</span> <span class="ident">attribute_per_case</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">val</span>: <span class="ident">i32</span>) {
+    <span class="kw">match</span> <span class="ident">val</span> {
+        <span class="number">0</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">assert!</span>(<span class="bool-val">true</span>),
+        <span class="number">1</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">panic!</span>(<span class="string">&quot;No catch&quot;</span>),
+        <span class="number">2</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">panic!</span>(<span class="string">&quot;expected&quot;</span>),
+        <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">unreachable!</span>(),
+    }
+}</pre></div>
+<p>Output:</p>
+<pre><code class="language-text">running 3 tests
+test attribute_per_case::case_1_no_panic ... ok
+test attribute_per_case::case_3_panic_with_message ... ok
+test attribute_per_case::case_2_panic ... ok
+
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+</code></pre>
+<p>To mark all your tests as <code>#[should_panic]</code> use:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">3</span>)]</span>
+<span class="attribute">#[<span class="ident">should_panic</span>]</span>
+<span class="kw">fn</span> <span class="ident">fail</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">v</span>: <span class="ident">u32</span>) { <span class="macro">assert_eq!</span>(<span class="number">0</span>, <span class="ident">v</span>) }</pre></div>
+<h2 id="values-lists" class="section-header"><a href="#values-lists">Values Lists</a></h2>
+<p>Another useful way to write a test and execute it for some values
+is to use the values list syntax. This syntax can be usefull both
+for a plain list and for testing all combination of input arguments.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_be_valid</span>(
+    <span class="attribute">#[<span class="ident">values</span>(<span class="string">&quot;Jhon&quot;</span>, <span class="string">&quot;alice&quot;</span>, <span class="string">&quot;My_Name&quot;</span>, <span class="string">&quot;Zigy_2001&quot;</span>)]</span>
+    <span class="ident">input</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>
+) {
+    <span class="macro">assert!</span>(<span class="ident">is_valid</span>(<span class="ident">input</span>))
+}</pre></div>
+<p>or</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_accept_all_corner_cases</span>(
+    <span class="attribute">#[<span class="ident">values</span>(<span class="string">&quot;J&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;A________________________________________21&quot;</span>)]</span>
+    <span class="ident">name</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>,
+    <span class="attribute">#[<span class="ident">values</span>(<span class="number">14</span>, <span class="number">100</span>)]</span>
+    <span class="ident">age</span>: <span class="ident">u8</span>
+) {
+    <span class="macro">assert!</span>(<span class="ident">valid_user</span>(<span class="ident">name</span>, <span class="ident">age</span>))
+}</pre></div>
+<p>where <code>cargo test</code> output is</p>
+<pre><code class="language-text">running 6 tests
+test should_accept_all_corner_cases::name_1::age_1 ... ok
+test should_accept_all_corner_cases::name_3::age_1 ... ok
+test should_accept_all_corner_cases::name_3::age_2 ... ok
+test should_accept_all_corner_cases::name_2::age_1 ... ok
+test should_accept_all_corner_cases::name_2::age_2 ... ok
+test should_accept_all_corner_cases::name_1::age_2 ... ok
+
+test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+</code></pre>
+<p>Also value list implements the magic conversion feature: every time the value type
+implements <code>FromStr</code> trait you can use a literal string to define it.</p>
+<h2 id="use-parametrize-definition-in-more-tests" class="section-header"><a href="#use-parametrize-definition-in-more-tests">Use Parametrize definition in more tests</a></h2>
+<p>If you need to use a test list for more than one test you can use
+<a href="https://crates.io/crates/rstest_reuse"><code>rstest_reuse</code></a> crate.
+With this helper crate you can define a template and use it everywhere.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">given_port</span>(<span class="attribute">#[<span class="ident">values</span>(<span class="string">&quot;1.2.3.4:8000&quot;</span>, <span class="string">&quot;4.3.2.1:8000&quot;</span>, <span class="string">&quot;127.0.0.1:8000&quot;</span>)]</span> <span class="ident">addr</span>: <span class="ident">SocketAddr</span>) {
+    <span class="ident">assert_eq</span>(<span class="number">8000</span>, <span class="ident">addr</span>.<span class="ident">port</span>())
+}</pre></div>
+
+<div class='information'><div class='tooltip ignore'>ⓘ</div></div><div class="example-wrap"><pre class="rust rust-example-rendered ignore">
+<span class="kw">use</span> <span class="ident">rstest::rstest</span>;
+<span class="kw">use</span> <span class="ident">rstest_reuse</span>::{<span class="self">self</span>, <span class="kw-2">*</span>};
+
+<span class="attribute">#[<span class="ident">template</span>]</span>
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>, <span class="number">2</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">4</span><span class="op">/</span><span class="number">2</span>, <span class="number">2</span>)]</span>
+<span class="kw">fn</span> <span class="ident">two_simple_cases</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">a</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">b</span>: <span class="ident">u32</span>) {}
+
+<span class="attribute">#[<span class="ident">apply</span>(<span class="ident">two_simple_cases</span>)]</span>
+<span class="kw">fn</span> <span class="ident">it_works</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">a</span>: <span class="ident">u32</span>,<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">b</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert!</span>(<span class="ident">a</span> <span class="op">=</span><span class="op">=</span> <span class="ident">b</span>);
+}</pre></div>
+<p>See <a href="https://crates.io/crates/rstest_reuse"><code>rstest_reuse</code></a> for more dettails.</p>
+<h2 id="async" class="section-header"><a href="#async">Async</a></h2>
+<p><code>rstest</code> provides out of the box <code>async</code> support. Just mark your
+test function as <code>async</code> and it’ll use <code>#[async-std::test]</code> to
+annotate it. This feature can be really useful to build async
+parametric tests using a tidy syntax:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">5</span>, <span class="number">2</span>, <span class="number">3</span>)]</span>
+<span class="attribute">#[<span class="ident">should_panic</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">42</span>, <span class="number">40</span>, <span class="number">1</span>)]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">my_async_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">a</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">b</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">async_sum</span>(<span class="ident">a</span>, <span class="ident">b</span>).<span class="kw">await</span>);
+}</pre></div>
+<p>Currently only <code>async-std</code> is supported out of the box. But if you need to use
+another runtime that provide it’s own test attribute (i.e. <code>tokio::test</code> or
+<code>actix_rt::test</code>) you can use it in your <code>async</code> test like described in
+<a href="attr.rstest.html#inject-test-attribute">Inject Test Attribute</a>.</p>
+<p>To use this feature, you need to enable <code>attributes</code> in the <code>async-std</code>
+features list in your <code>Cargo.toml</code>:</p>
+<pre><code class="language-toml">async-std = { version = &quot;1.5&quot;, features = [&quot;attributes&quot;] }
+</code></pre>
+<p>If your test input is an async value (fixture or test parameter) you can use <code>#[future]</code>
+attribute to remove <code>impl Future&lt;Output = T&gt;</code> boilerplate and just use <code>T</code>:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">base</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">u32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">21</span>, <span class="kw">async</span> { <span class="number">2</span> })]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">6</span>, <span class="kw">async</span> { <span class="number">7</span> })]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">my_async_test</span>(<span class="attribute">#[<span class="ident">future</span>]</span> <span class="ident">base</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">future</span>]</span> <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">div</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">base</span>.<span class="kw">await</span> <span class="op">/</span> <span class="ident">div</span>.<span class="kw">await</span>);
+}</pre></div>
+<h2 id="inject-test-attribute" class="section-header"><a href="#inject-test-attribute">Inject Test Attribute</a></h2>
+<p>If you would like to use another <code>test</code> attribute for your test you can simply
+indicate it in your test function’s attributes. For instance if you want
+to test some async function with use <code>actix_rt::test</code> attribute you can just write:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+<span class="kw">use</span> <span class="ident">actix_rt</span>;
+<span class="kw">use</span> <span class="ident">std::future::Future</span>;
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>, <span class="kw">async</span> { <span class="number">4</span> })]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">21</span>, <span class="kw">async</span> { <span class="number">42</span> })]</span>
+<span class="attribute">#[<span class="ident">actix_rt::test</span>]</span>
+<span class="kw">async</span> <span class="kw">fn</span> <span class="ident">my_async_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">a</span>: <span class="ident">u32</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="attribute">#[<span class="ident">future</span>]</span> <span class="ident">result</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">2</span> <span class="op">*</span> <span class="ident">a</span>, <span class="ident">result</span>.<span class="kw">await</span>);
+}</pre></div>
+<p>Just the attributes that ends with <code>test</code> (last path segment) can be injected:
+in this case the <code>#[actix_rt::test]</code> attribute will replace the standard <code>#[test]</code>
+attribute.</p>
+<h2 id="putting-all-together" class="section-header"><a href="#putting-all-together">Putting all Together</a></h2>
+<p>All these features can be used together with a mixture of fixture variables,
+fixed cases and bunch of values. For instance, you might need two
+test cases which test for panics, one for a logged in user and one for a guest user.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">repository</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">InMemoryRepository</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">r</span> <span class="op">=</span> <span class="ident">InMemoryRepository::default</span>();
+    <span class="comment">// fill repository with some data</span>
+    <span class="ident">r</span>
+}
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">alice</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">User</span> {
+    <span class="ident">User::logged</span>(<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;2001-10-04&quot;</span>, <span class="string">&quot;London&quot;</span>, <span class="string">&quot;UK&quot;</span>)
+}
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case::authed_user</span>(<span class="ident">alice</span>())]</span> <span class="comment">// We can use `fixture` also as standard function</span>
+<span class="attribute">#[<span class="ident">case::guest</span>(<span class="ident">User::Guest</span>)]</span>   <span class="comment">// We can give a name to every case : `guest` in this case</span>
+<span class="attribute">#[<span class="ident">should_panic</span>(<span class="ident">expected</span> <span class="op">=</span> <span class="string">&quot;Invalid query error&quot;</span>)]</span> <span class="comment">// We whould test a panic</span>
+<span class="kw">fn</span> <span class="ident">should_be_invalid_query_error</span>(
+    <span class="ident">repository</span>: <span class="kw">impl</span> <span class="ident">Repository</span>,
+    <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">user</span>: <span class="ident">User</span>,
+    <span class="attribute">#[<span class="ident">values</span>(<span class="string">&quot;     &quot;</span>, <span class="string">&quot;^%$#@!&quot;</span>, <span class="string">&quot;....&quot;</span>)]</span>
+    <span class="ident">query</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>
+) {
+    <span class="ident">repository</span>.<span class="ident">find_items</span>(<span class="kw-2">&amp;</span><span class="ident">user</span>, <span class="ident">query</span>).<span class="ident">unwrap</span>();
+}</pre></div>
+<h2 id="trace-input-arguments" class="section-header"><a href="#trace-input-arguments">Trace Input Arguments</a></h2>
+<p>Sometimes can be very helpful to print all test’s input arguments. To
+do it you can use the <code>#[trace]</code> function attribute that you can apply
+to all cases or just to some of them.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest</span>::<span class="kw-2">*</span>;
+
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">injected</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">trace</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<p>Will print an output like</p>
+<pre><code class="language-bash">Testing started at 14.12 ...
+------------ TEST ARGUMENTS ------------
+injected = 42
+-------------- TEST START --------------
+
+
+Expected :42
+Actual   :43
+</code></pre>
+<p>But</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">trace</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>)]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">v</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">0</span>, <span class="ident">v</span>)
+}</pre></div>
+<p>will trace just <code>case_2</code> input arguments.</p>
+<p>If you want to trace input arguments but skip some of them that don’t
+implement the <code>Debug</code> trait, you can also use the
+<code>#[notrace]</code> argument attribute to skip them:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">trace</span>]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>, <span class="attribute">#[<span class="ident">notrace</span>]</span> <span class="ident">xyz</span>: <span class="ident">Xyz</span>, <span class="attribute">#[<span class="ident">notrace</span>]</span> <span class="ident">have_no_sense</span>: <span class="ident">NoSense</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+<h1 id="old-compact-syntax" class="section-header"><a href="#old-compact-syntax">Old <em>compact</em> syntax</a></h1>
+<p><code>rstest</code> support also a syntax where all options and configuration can be write as
+<code>rstest</code> attribute arguments. This syntax is a little less verbose but make
+composition harder: for istance try to add some cases to a <code>rstest_reuse</code> template
+is really hard.</p>
+<p>So we’ll continue to maintain the old syntax for a long time but we strongly encourage
+to switch your test in the new form.</p>
+<p>Anyway, here we recall this syntax and rewrite the previous example in the <em>compact</em> form.</p>
+<pre><code class="language-text">rstest(
+    arg_1,
+    ...,
+    arg_n[,]
+    [::attribute_1[:: ... [::attribute_k]]]
+)
+</code></pre>
+<p>Where:</p>
+<ul>
+<li><code>arg_i</code> could be one of the follow
+<ul>
+<li><code>ident</code> that match to one of function arguments for parametrized cases</li>
+<li><code>case[::description](v1, ..., vl)</code> a test case</li>
+<li><code>fixture(v1, ..., vl) [as argument_name]</code> where fixture is the injected
+fixture and argument_name (default use fixture) is one of function arguments
+that and <code>v1, ..., vl</code> is a partial list of fixture’s arguments</li>
+<li><code>ident =&gt; [v1, ..., vl]</code> where <code>ident</code> is one of function arguments and
+<code>v1, ..., vl</code> is a list of values for ident</li>
+</ul>
+</li>
+<li><code>attribute_j</code> a test attribute like <code>trace</code> or <code>notrace</code></li>
+</ul>
+<h2 id="fixture-arguments" class="section-header"><a href="#fixture-arguments">Fixture Arguments</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>(<span class="ident">user</span>(<span class="string">&quot;Bob&quot;</span>))]</span>
+<span class="kw">fn</span> <span class="ident">check_user</span>(<span class="ident">user</span>: <span class="ident">User</span>) {
+    <span class="ident">assert_eq</span>(<span class="string">&quot;Bob&quot;</span>, <span class="ident">user</span>.<span class="ident">name</span>())
+}</pre></div>
+<h2 id="fixture-rename" class="section-header"><a href="#fixture-rename">Fixture Rename</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">long_and_boring_descriptive_name</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="ident">i32</span> { <span class="number">42</span> }
+
+<span class="attribute">#[<span class="ident">rstest</span>(<span class="ident">long_and_boring_descriptive_name</span> <span class="kw">as</span> <span class="ident">short</span>)]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">short</span>: <span class="ident">i32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">short</span>)
+}</pre></div>
+<h2 id="parametrized" class="section-header"><a href="#parametrized">Parametrized</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>(<span class="ident">input</span>, <span class="ident">expected</span>,
+    <span class="ident">case::zero_base_case</span>(<span class="number">0</span>, <span class="number">0</span>),
+    <span class="ident">case::one_base_case</span>(<span class="number">1</span>, <span class="number">1</span>),
+    <span class="ident">case</span>(<span class="number">2</span>, <span class="number">1</span>),
+    <span class="ident">case</span>(<span class="number">3</span>, <span class="number">2</span>),
+    <span class="attribute">#[<span class="ident">should_panic</span>]</span>
+    <span class="ident">case</span>(<span class="number">4</span>, <span class="number">42</span>)
+)]
+<span class="kw">fn</span> <span class="ident">fibonacci_test</span>(<span class="ident">input</span>: <span class="ident">u32</span>, <span class="ident">expected</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">fibonacci</span>(<span class="ident">input</span>))
+}
+</pre></div>
+<h2 id="values-lists-1" class="section-header"><a href="#values-lists-1">Values Lists</a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+
+<span class="attribute">#[<span class="ident">rstest</span>(
+    <span class="ident">input</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="string">&quot;Jhon&quot;</span>, <span class="string">&quot;alice&quot;</span>, <span class="string">&quot;My_Name&quot;</span>, <span class="string">&quot;Zigy_2001&quot;</span>]</span>
+)]
+<span class="kw">fn</span> <span class="ident">should_be_valid</span>(<span class="ident">input</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+    <span class="macro">assert!</span>(<span class="ident">is_valid</span>(<span class="ident">input</span>))
+}</pre></div>
+<h2 id="trace-and-notrace" class="section-header"><a href="#trace-and-notrace"><code>trace</code> and <code>notrace</code></a></h2>
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>(<span class="ident">::trace::notrace</span>(<span class="ident">xzy</span>, <span class="ident">have_no_sense</span>))]</span>
+<span class="kw">fn</span> <span class="ident">the_test</span>(<span class="ident">injected</span>: <span class="ident">i32</span>, <span class="ident">xyz</span>: <span class="ident">Xyz</span>, <span class="ident">have_no_sense</span>: <span class="ident">NoSense</span>) {
+    <span class="macro">assert_eq!</span>(<span class="number">42</span>, <span class="ident">injected</span>)
+}</pre></div>
+</div></details></section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../" data-current-crate="rstest" data-search-index-js="../search-index.js" data-search-js="../search.js"></div>
+    <script src="../main.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_matrix.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_matrix.html
new file mode 100644
index 0000000..947b738
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_matrix.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `rstest_matrix` attr in crate `rstest`."><meta name="keywords" content="rust, rustlang, rust-lang, rstest_matrix"><title>rstest::rstest_matrix - Rust</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../dark.css"><link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle"><script src="../storage.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="shortcut icon" href="../favicon.ico"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc attr"><!--[if lte IE 8]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu">&#9776;</div><a href='../rstest/index.html'><div class='logo-container'><img src='../rust-logo.png' alt='logo'></div></a><div class="sidebar-elems"><p class='location'><a href='index.html'>rstest</a></p><script>window.sidebarCurrent = {name: 'rstest_matrix', ty: 'attr', relpath: ''};</script><script defer src="sidebar-items.js"></script></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg" width="18" alt="Pick another theme!"></button><div id="theme-choices"></div></div><script src="../theme.js"></script><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class='fqn'><span class='out-of-band'><span id='render-detail'><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class='inner'>&#x2212;</span>]</a></span><a class='srclink' href='../src/rstest/lib.rs.html#838-843' title='goto source code'>[src]</a></span><span class='in-band'>Attribute Macro <a href='index.html'>rstest</a>::<wbr><a class="attr" href=''>rstest_matrix</a></span></h1><pre class='rust attr'>#[rstest_matrix]</pre><div class='stability'><div class='stab deprecated'>Deprecated since 0.5.0: <p>Please use #[rstest(...)] instead</p>
+</div></div><div class='docblock'><p>Write matrix-based tests: you must indicate arguments and values list that you want to test and
+<code>rstest_matrix</code> generate an indipendent test for each argument combination (the carthesian
+product of values lists).</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest_matrix</span>(
+    <span class="ident">foo</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="number">42</span>, <span class="number">24</span>]</span>,
+    <span class="ident">bar</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="string">&quot;foo&quot;</span>, <span class="string">&quot;bar&quot;</span>]
+)]
+<span class="kw">fn</span> <span class="ident">matrix_test</span>(<span class="ident">foo</span>: <span class="ident">u32</span>, <span class="ident">bar</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+    <span class="comment">//... test body</span>
+}</pre></div>
+<p>Running <code>cargo test</code> in this case executes four tests:</p>
+<pre><code class="language-bash">running 4 tests
+test matrix_test::case_1_1 ... ok
+test matrix_test::case_1_2 ... ok
+test matrix_test::case_2_1 ... ok
+test matrix_test::case_2_2 ... ok
+
+test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+
+</code></pre>
+<p>Like in <a href="../rstest/attr.rstest.html"><code>[rstest]</code></a> you can inject fixture values and every parameter that
+isn't mapped in <code>case()</code>s will be resolved as default <code>fixture</code>.</p>
+<p>In general <code>rstest_matrix</code>'s syntax is:</p>
+<pre><code class="language-norun">rstest_matrix(
+    ident_1 =&gt; [val_1_1, ..., val_1_m1],
+    ....
+    ident_n =&gt; [val_n_1, ..., val_n_mn][,]
+    [fixture_1(...]
+    [...,]
+    [fixture_k(...)]
+    [::attribute_1[:: ... [::attribute_k]]]
+)
+</code></pre>
+<ul>
+<li><code>ident_x</code> should be a valid function argument name</li>
+<li><code>val_x_y</code> should be a valid rust expression that can be assigned to <code>ident_x</code> function argument</li>
+<li><code>fixture_v(...)</code> should be a valid function argument and a <a href="../rstest/attr.fixture.html"><code>[fixture]</code></a> fixture function</li>
+<li>attributes now can be just <code>trace</code> or <code>notrace(args..)</code> (see <a href="../rstest/attr.rstest.html"><code>[rstest]</code></a></li>
+</ul>
+<p>Function's arguments can be present just once as identity or fixture.</p>
+<p>Functions marked by <code>rstest_matrix</code> can use generics, <code>impl</code> and <code>dyn</code> without any
+restriction.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest_matrix</span>(
+    <span class="ident">foo</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="string">&quot;foo&quot;</span>, <span class="ident">String</span>::<span class="ident">from</span>(<span class="string">&quot;foo&quot;</span>)]</span>
+)]
+<span class="kw">fn</span> <span class="ident">len</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">foo</span>: <span class="ident">S</span>) {
+    <span class="macro">assert_eq</span><span class="macro">!</span>(<span class="number">3</span>, <span class="ident">input</span>.<span class="ident">as_ref</span>().<span class="ident">len</span>())
+}
+
+<span class="attribute">#[<span class="ident">rstest_matrix</span>(
+    <span class="ident">foo</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="string">&quot;foo&quot;</span>, <span class="ident">String</span>::<span class="ident">from</span>(<span class="string">&quot;foo&quot;</span>)]</span>
+)]
+<span class="kw">fn</span> <span class="ident">len_by_impl</span>(<span class="ident">foo</span>: <span class="kw">impl</span> <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span>) {
+    <span class="macro">assert_eq</span><span class="macro">!</span>(<span class="number">3</span>, <span class="ident">input</span>.<span class="ident">as_ref</span>().<span class="ident">len</span>())
+}</pre></div>
+</div></section><section id="search" class="content hidden"></section><section class="footer"></section><script>window.rootPath = "../";window.currentCrate = "rstest";</script><script src="../aliases.js"></script><script src="../main.js"></script><script defer src="../search-index.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_parametrize.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_parametrize.html
new file mode 100644
index 0000000..4bad22c69
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/attr.rstest_parametrize.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `rstest_parametrize` attr in crate `rstest`."><meta name="keywords" content="rust, rustlang, rust-lang, rstest_parametrize"><title>rstest::rstest_parametrize - Rust</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../dark.css"><link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle"><script src="../storage.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="shortcut icon" href="../favicon.ico"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc attr"><!--[if lte IE 8]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu">&#9776;</div><a href='../rstest/index.html'><div class='logo-container'><img src='../rust-logo.png' alt='logo'></div></a><div class="sidebar-elems"><p class='location'><a href='index.html'>rstest</a></p><script>window.sidebarCurrent = {name: 'rstest_parametrize', ty: 'attr', relpath: ''};</script><script defer src="sidebar-items.js"></script></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg" width="18" alt="Pick another theme!"></button><div id="theme-choices"></div></div><script src="../theme.js"></script><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class='fqn'><span class='out-of-band'><span id='render-detail'><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class='inner'>&#x2212;</span>]</a></span><a class='srclink' href='../src/rstest/lib.rs.html#756-761' title='goto source code'>[src]</a></span><span class='in-band'>Attribute Macro <a href='index.html'>rstest</a>::<wbr><a class="attr" href=''>rstest_parametrize</a></span></h1><pre class='rust attr'>#[rstest_parametrize]</pre><div class='stability'><div class='stab deprecated'>Deprecated since 0.5.0: <p>Please use #[rstest(...)] instead</p>
+</div></div><div class='docblock'><p>Write table-based tests: you must indicate the arguments that you want use in your cases
+and provide them for each case you want to test.</p>
+<p><code>rstest_parametrize</code> generates an independent test for each case.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest_parametrize</span>(<span class="ident">input</span>, <span class="ident">expected</span>,
+    <span class="ident">case</span>(<span class="number">0</span>, <span class="number">0</span>),
+    <span class="ident">case</span>(<span class="number">1</span>, <span class="number">1</span>),
+    <span class="ident">case</span>(<span class="number">2</span>, <span class="number">1</span>),
+    <span class="ident">case</span>(<span class="number">3</span>, <span class="number">2</span>),
+    <span class="ident">case</span>(<span class="number">4</span>, <span class="number">3</span>)
+)]</span>
+<span class="kw">fn</span> <span class="ident">fibonacci_test</span>(<span class="ident">input</span>: <span class="ident">u32</span>, <span class="ident">expected</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq</span><span class="macro">!</span>(<span class="ident">expected</span>, <span class="ident">fibonacci</span>(<span class="ident">input</span>))
+}</pre></div>
+<p>Running <code>cargo test</code> in this case executes five tests:</p>
+<pre><code class="language-bash">running 5 tests
+test fibonacci_test::case_1 ... ok
+test fibonacci_test::case_2 ... ok
+test fibonacci_test::case_3 ... ok
+test fibonacci_test::case_4 ... ok
+test fibonacci_test::case_5 ... ok
+
+test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+</code></pre>
+<p>Like in <a href="../rstest/attr.rstest.html"><code>[rstest]</code></a> you can inject fixture values and every parameter that
+isn't mapped in <code>case()</code>s will be resolved as default <code>fixture</code>.</p>
+<p>In general <code>rstest_parametrize</code>'s syntax is:</p>
+<pre><code class="language-norun">rstest_parametrize(ident_1,..., ident_n,
+    case[::description_1](val_1_1, ..., val_n_1),
+    ...,
+    case[::description_m](val_1_m, ..., val_n_m)[,]
+    [fixture_1(...]
+    [...,]
+    [fixture_k(...)]
+    [::attribute_1[:: ... [::attribute_k]]]
+)
+</code></pre>
+<ul>
+<li><code>ident_x</code> should be a valid function argument name</li>
+<li><code>val_x_y</code> should be a valid rust expression that can be assigned to <code>ident_x</code> function argument</li>
+<li><code>description_z</code> when present should be a valid Rust identity</li>
+<li><code>fixture_v(...)</code> should be a valid function argument and a <a href="../rstest/attr.fixture.html"><code>[fixture]</code></a> fixture function</li>
+<li>attributes now can be just <code>trace</code> or <code>notrace(args..)</code> (see <a href="../rstest/attr.rstest.html"><code>[rstest]</code></a></li>
+</ul>
+<p>Function's arguments can be present just once as identity or fixture.</p>
+<p>Functions marked by <code>rstest_parametrize</code> can use generics, <code>impl</code> and <code>dyn</code> without any
+restriction.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest_parametrize</span>(<span class="ident">input</span>, <span class="ident">expected</span>,
+    <span class="ident">case</span>(<span class="string">&quot;foo&quot;</span>, <span class="number">3</span>),
+    <span class="ident">case</span>(<span class="ident">String</span>::<span class="ident">from</span>(<span class="string">&quot;bar&quot;</span>), <span class="number">3</span>),
+)]</span>
+<span class="kw">fn</span> <span class="ident">len</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">input</span>: <span class="ident">S</span>, <span class="ident">expected</span>: <span class="ident">usize</span>) {
+    <span class="macro">assert_eq</span><span class="macro">!</span>(<span class="ident">expected</span>, <span class="ident">input</span>.<span class="ident">as_ref</span>().<span class="ident">len</span>())
+}
+
+<span class="attribute">#[<span class="ident">rstest_parametrize</span>(<span class="ident">input</span>, <span class="ident">expected</span>,
+    <span class="ident">case</span>(<span class="string">&quot;foo&quot;</span>, <span class="number">3</span>),
+    <span class="ident">case</span>(<span class="ident">String</span>::<span class="ident">from</span>(<span class="string">&quot;bar&quot;</span>), <span class="number">3</span>),
+)]</span>
+<span class="kw">fn</span> <span class="ident">len_by_impl</span>(<span class="ident">input</span>: <span class="kw">impl</span> <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span>, <span class="ident">expected</span>: <span class="ident">usize</span>) {
+    <span class="macro">assert_eq</span><span class="macro">!</span>(<span class="ident">expected</span>, <span class="ident">input</span>.<span class="ident">as_ref</span>().<span class="ident">len</span>())
+}</pre></div>
+</div></section><section id="search" class="content hidden"></section><section class="footer"></section><script>window.rootPath = "../";window.currentCrate = "rstest";</script><script src="../aliases.js"></script><script src="../main.js"></script><script defer src="../search-index.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/index.html b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/index.html
new file mode 100644
index 0000000..58347062
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/index.html
@@ -0,0 +1,186 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="This crate will help you to write simpler tests by leveraging a software testing concept called test fixtures. A fixture is something that you can use in your tests to encapsulate a test’s dependencies."><meta name="keywords" content="rust, rustlang, rust-lang, rstest"><title>rstest - Rust</title><link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../ayu.css" disabled ><script id="default-settings"></script><script src="../storage.js"></script><script src="../crates.js"></script><noscript><link rel="stylesheet" href="../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../down-arrow.svg");}</style></head><body class="rustdoc mod"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../rstest/index.html'><div class='logo-container rust-logo'><img src='../rust-logo.png' alt='logo'></div></a><p class="location">Crate rstest</p><div class="block version"><p>Version 0.11.0</p></div><div class="sidebar-elems"><a id="all-types" href="all.html"><p>See all rstest's items</p></a><p class="location"></p><div id="sidebar-vars" data-name="rstest" data-ty="mod" data-relpath="../"></div></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../settings.html"><img src="../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class="fqn"><span class="in-band">Crate <a class="mod" href="">rstest</a><button id="copy-path" onclick="copy_path(this)"><img src="../clipboard.svg" width="19" height="18" alt="Copy item import"></button></span><span class="out-of-band"><span id="render-detail"><a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">[<span class="inner">&#x2212;</span>]</a></span><a class="srclink" href="../src/rstest/lib.rs.html#1-1190" title="goto source code">[src]</a></span></h1><details class="rustdoc-toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>This crate will help you to write simpler tests by leveraging a software testing concept called
+<a href="https://en.wikipedia.org/wiki/Test_fixture#Software">test fixtures</a>. A fixture is something
+that you can use in your tests to encapsulate a test’s dependencies.</p>
+<p>The general idea is to have smaller tests that only describe the thing you’re testing while you
+hide the auxiliary utilities your tests make use of somewhere else.
+For instance, if you have an application that has many tests with users, shopping baskets, and
+products, you’d have to create a user, a shopping basket, and product every single time in
+every test which becomes unwieldy quickly. In order to cut down on that repetition, you can
+instead use fixtures to declare that you need those objects for your function and the fixtures
+will take care of creating those by themselves. Focus on the important stuff in your tests!</p>
+<p>In <code>rstest</code> a fixture is a function that can return any kind of valid Rust type. This
+effectively means that your fixtures are not limited by the kind of data they can return.
+A test can consume an arbitrary number of fixtures at the same time.</p>
+<h2 id="what" class="section-header"><a href="#what">What</a></h2>
+<p>The <code>rstest</code> crate defines the following procedural macros:</p>
+<ul>
+<li><a href="attr.rstest.html"><code>[rstest]</code></a>: Declare that a test or a group of tests that may take
+<a href="attr.rstest.html#injecting-fixtures">fixtures</a>,
+<a href="attr.rstest.html#test-parametrized-cases">input table</a> or
+<a href="attr.rstest.html#values-lists">list of values</a>.</li>
+<li><a href="attr.fixture.html"><code>[fixture]</code></a>: To mark a function as a fixture.</li>
+</ul>
+<h2 id="why" class="section-header"><a href="#why">Why</a></h2>
+<p>Very often in Rust we write tests like this</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">test</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_process_two_users</span>() {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">repository</span> <span class="op">=</span> <span class="ident">create_repository</span>();
+    <span class="ident">repository</span>.<span class="ident">add</span>(<span class="string">&quot;Bob&quot;</span>, <span class="number">21</span>);
+    <span class="ident">repository</span>.<span class="ident">add</span>(<span class="string">&quot;Alice&quot;</span>, <span class="number">22</span>);
+
+    <span class="kw">let</span> <span class="ident">processor</span> <span class="op">=</span> <span class="ident">string_processor</span>();
+    <span class="ident">processor</span>.<span class="ident">send_all</span>(<span class="kw-2">&amp;</span><span class="ident">repository</span>, <span class="string">&quot;Good Morning&quot;</span>);
+
+    <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">processor</span>.<span class="ident">output</span>.<span class="ident">find</span>(<span class="string">&quot;Good Morning&quot;</span>).<span class="ident">count</span>());
+    <span class="macro">assert!</span>(<span class="ident">processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Bob&quot;</span>));
+    <span class="macro">assert!</span>(<span class="ident">processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Alice&quot;</span>));
+}</pre></div>
+<p>By making use of <a href="attr.rstest.html"><code>[rstest]</code></a> we can isolate the dependencies <code>empty_repository</code> and
+<code>string_processor</code> by passing them as fixtures:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_process_two_users</span>(<span class="kw-2">mut</span> <span class="ident">empty_repository</span>: <span class="kw">impl</span> <span class="ident">Repository</span>,
+                            <span class="ident">string_processor</span>: <span class="ident">FakeProcessor</span>) {
+    <span class="ident">empty_repository</span>.<span class="ident">add</span>(<span class="string">&quot;Bob&quot;</span>, <span class="number">21</span>);
+    <span class="ident">empty_repository</span>.<span class="ident">add</span>(<span class="string">&quot;Alice&quot;</span>, <span class="number">22</span>);
+
+    <span class="ident">string_processor</span>.<span class="ident">send_all</span>(<span class="string">&quot;Good Morning&quot;</span>);
+
+    <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">find</span>(<span class="string">&quot;Good Morning&quot;</span>).<span class="ident">count</span>());
+    <span class="macro">assert!</span>(<span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Bob&quot;</span>));
+    <span class="macro">assert!</span>(<span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Alice&quot;</span>));
+}</pre></div>
+<p>… or if you use <code>&quot;Alice&quot;</code> and <code>&quot;Bob&quot;</code> in other tests, you can isolate <code>alice_and_bob</code> fixture
+and use it directly:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">fixture</span>]</span>
+<span class="kw">fn</span> <span class="ident">alice_and_bob</span>(<span class="kw-2">mut</span> <span class="ident">empty_repository</span>: <span class="kw">impl</span> <span class="ident">Repository</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Repository</span> {
+    <span class="ident">empty_repository</span>.<span class="ident">add</span>(<span class="string">&quot;Bob&quot;</span>, <span class="number">21</span>);
+    <span class="ident">empty_repository</span>.<span class="ident">add</span>(<span class="string">&quot;Alice&quot;</span>, <span class="number">22</span>);
+    <span class="ident">empty_repository</span>
+}
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_process_two_users</span>(<span class="ident">alice_and_bob</span>: <span class="kw">impl</span> <span class="ident">Repository</span>,
+                            <span class="ident">string_processor</span>: <span class="ident">FakeProcessor</span>) {
+    <span class="ident">string_processor</span>.<span class="ident">send_all</span>(<span class="string">&quot;Good Morning&quot;</span>);
+
+    <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">find</span>(<span class="string">&quot;Good Morning&quot;</span>).<span class="ident">count</span>());
+    <span class="macro">assert!</span>(<span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Bob&quot;</span>));
+    <span class="macro">assert!</span>(<span class="ident">string_processor</span>.<span class="ident">output</span>.<span class="ident">contains</span>(<span class="string">&quot;Alice&quot;</span>));
+}</pre></div>
+<h2 id="injecting-fixtures-as-function-arguments" class="section-header"><a href="#injecting-fixtures-as-function-arguments">Injecting fixtures as function arguments</a></h2>
+<p><code>rstest</code> functions can receive fixtures by using them as input arguments.
+A function decorated with <a href="attr.rstest.html#injecting-fixtures"><code>[rstest]</code></a>
+will resolve each argument name by call the fixture function.
+Fixtures should be annotated with the <a href="attr.fixture.html"><code>[fixture]</code></a> attribute.</p>
+<p>Fixtures will be resolved like function calls by following the standard resolution rules.
+Therefore, an identically named fixture can be use in different context.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">mod</span> <span class="ident">empty_cases</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+    <span class="attribute">#[<span class="ident">fixture</span>]</span>
+    <span class="kw">fn</span> <span class="ident">repository</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Repository</span> {
+        <span class="ident">DataSet::default</span>()
+    }
+
+    <span class="attribute">#[<span class="ident">rstest</span>]</span>
+    <span class="kw">fn</span> <span class="ident">should_do_nothing</span>(<span class="ident">repository</span>: <span class="kw">impl</span> <span class="ident">Repository</span>) {
+        <span class="comment">//.. test impl ..</span>
+    }
+}
+
+<span class="kw">mod</span> <span class="ident">non_trivial_case</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+    <span class="attribute">#[<span class="ident">fixture</span>]</span>
+    <span class="kw">fn</span> <span class="ident">repository</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Repository</span> {
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">ds</span> <span class="op">=</span> <span class="ident">DataSet::default</span>();
+        <span class="comment">// Fill your dataset with interesting case</span>
+        <span class="ident">ds</span>
+    }
+
+    <span class="attribute">#[<span class="ident">rstest</span>]</span>
+    <span class="kw">fn</span> <span class="ident">should_notify_all_entries</span>(<span class="ident">repository</span>: <span class="kw">impl</span> <span class="ident">Repository</span>) {
+        <span class="comment">//.. test impl ..</span>
+    }
+}
+</pre></div>
+<p>Last but not least, fixtures can be injected like we saw in <code>alice_and_bob</code> example.</p>
+<h2 id="creating-parametrized-tests" class="section-header"><a href="#creating-parametrized-tests">Creating parametrized tests</a></h2>
+<p>You can use also <a href="attr.rstest.html#test-parametrized-cases"><code>[rstest]</code></a> to create
+simple table-based tests. Let’s see the classic Fibonacci example:</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="kw">use</span> <span class="ident">rstest::rstest</span>;
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">0</span>, <span class="number">0</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">1</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">2</span>, <span class="number">1</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">3</span>, <span class="number">2</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">4</span>, <span class="number">3</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">5</span>, <span class="number">5</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="number">6</span>, <span class="number">8</span>)]</span>
+<span class="kw">fn</span> <span class="ident">fibonacci_test</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">input</span>: <span class="ident">u32</span>,<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u32</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">fibonacci</span>(<span class="ident">input</span>))
+}
+
+<span class="kw">fn</span> <span class="ident">fibonacci</span>(<span class="ident">input</span>: <span class="ident">u32</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">u32</span> {
+    <span class="kw">match</span> <span class="ident">input</span> {
+        <span class="number">0</span> <span class="op">=</span><span class="op">&gt;</span> <span class="number">0</span>,
+        <span class="number">1</span> <span class="op">=</span><span class="op">&gt;</span> <span class="number">1</span>,
+        <span class="ident">n</span> <span class="op">=</span><span class="op">&gt;</span> <span class="ident">fibonacci</span>(<span class="ident">n</span> <span class="op">-</span> <span class="number">2</span>) <span class="op">+</span> <span class="ident">fibonacci</span>(<span class="ident">n</span> <span class="op">-</span> <span class="number">1</span>)
+    }
+}</pre></div>
+<p>This will generate a bunch of tests, one for every <code>#[case(a, b)]</code>.</p>
+<h2 id="creating-a-test-for-each-combinations-of-given-values" class="section-header"><a href="#creating-a-test-for-each-combinations-of-given-values">Creating a test for each combinations of given values</a></h2>
+<p>In some cases you need to test your code for each combinations of some input values. In this
+cases <a href="attr.rstest.html#values-lists"><code>[rstest]</code></a> give you the ability to define a list
+of values (rust expressions) to use for an arguments.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="kw">fn</span> <span class="ident">should_terminate</span>(
+    <span class="attribute">#[<span class="ident">values</span>(<span class="ident">State::Init</span>, <span class="ident">State::Start</span>, <span class="ident">State::Processing</span>)]</span>
+    <span class="ident">state</span>: <span class="ident">State</span>,
+    <span class="attribute">#[<span class="ident">values</span>(<span class="ident">Event::Error</span>, <span class="ident">Event::Fatal</span>)]</span>
+    <span class="ident">event</span>: <span class="ident">Event</span>
+) {
+    <span class="macro">assert_eq!</span>(<span class="ident">State::Terminated</span>, <span class="ident">state</span>.<span class="ident">process</span>(<span class="ident">event</span>))
+}</pre></div>
+<p>This will generate a test for each combination of <code>state</code> and <code>event</code>.</p>
+<h2 id="magic-conversion" class="section-header"><a href="#magic-conversion">Magic Conversion</a></h2>
+<p>If you need a value where its type implement <code>FromStr()</code> trait you
+can use a literal string to build it.</p>
+
+<div class="example-wrap"><pre class="rust rust-example-rendered">
+<span class="attribute">#[<span class="ident">rstest</span>]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;1.2.3.4:8080&quot;</span>, <span class="number">8080</span>)]</span>
+<span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;127.0.0.1:9000&quot;</span>, <span class="number">9000</span>)]</span>
+<span class="kw">fn</span> <span class="ident">check_port</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">addr</span>: <span class="ident">SocketAddr</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">u16</span>) {
+    <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">addr</span>.<span class="ident">port</span>());
+}</pre></div>
+<p>You can use this feature also in value list and in fixture default value.</p>
+</div></details><h2 id="attributes" class="section-header"><a href="#attributes">Attribute Macros</a></h2>
+<table><tr class="module-item"><td><a class="attr" href="attr.fixture.html" title="rstest::fixture attr">fixture</a></td><td class="docblock-short"><p>Define a fixture that you can use in all <code>rstest</code>’s test arguments. You should just mark your
+function as <code>#[fixture]</code> and then use it as a test’s argument. Fixture functions can also
+use other fixtures.</p>
+</td></tr><tr class="module-item"><td><a class="attr" href="attr.rstest.html" title="rstest::rstest attr">rstest</a></td><td class="docblock-short"><p>The attribute that you should use for your tests. Your
+annotated function’s arguments can be
+<a href="attr.rstest.html#injecting-fixtures">injected</a> with
+<a href="attr.fixture.html"><code>[fixture]</code></a>s, provided by
+<a href="attr.rstest.html#test-parametrized-cases">parametrized cases</a>
+or by <a href="attr.rstest.html#values-lists">value lists</a>.</p>
+</td></tr></table></section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../" data-current-crate="rstest" data-search-index-js="../search-index.js" data-search-js="../search.js"></div>
+    <script src="../main.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rstest/sidebar-items.js b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/sidebar-items.js
new file mode 100644
index 0000000..205d613
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rstest/sidebar-items.js
@@ -0,0 +1 @@
+initSidebarItems({"attr":[["fixture","Define a fixture that you can use in all `rstest`’s test arguments. You should just mark your function as `#[fixture]` and then use it as a test’s argument. Fixture functions can also use other fixtures."],["rstest","The attribute that you should use for your tests. Your annotated function’s arguments can be injected with `[fixture]`s, provided by parametrized cases or by value lists."]]});
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rust-logo.png b/third_party/rust/rstest/v0_12/crate/docs/head/rust-logo.png
new file mode 100644
index 0000000..74b4bd69
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rust-logo.png
Binary files differ
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/rustdoc.css b/third_party/rust/rstest/v0_12/crate/docs/head/rustdoc.css
new file mode 100644
index 0000000..5c4877c
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/rustdoc.css
@@ -0,0 +1 @@
+ @font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular.woff2") format("woff2"),url("FiraSans-Regular.woff") format('woff');font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium.woff2") format("woff2"),url("FiraSans-Medium.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular.ttf.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It.ttf.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold.ttf.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular.ttf.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It.ttf.woff") format('woff');font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold.ttf.woff") format('woff');font-display:swap;}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}html{content:"";}@media (prefers-color-scheme:light){html{content:"light";}}@media (prefers-color-scheme:dark){html{content:"dark";}}body{font:16px/1.4 "Source Serif 4",serif;margin:0;position:relative;padding:10px 15px 20px 15px;-webkit-font-feature-settings:"kern","liga";-moz-font-feature-settings:"kern","liga";font-feature-settings:"kern","liga";}h1{font-size:1.5em;}h2{font-size:1.4em;}h3{font-size:1.3em;}h1,h2,h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.notable),h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant):not(.associatedtype){font-weight:500;margin:20px 0 15px 0;padding-bottom:6px;}h1.fqn{display:flex;border-bottom:1px dashed;margin-top:0;padding-left:1px;}h1.fqn>.in-band>a:hover{text-decoration:underline;}h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant):not(.associatedtype){border-bottom:1px solid;}h3.impl,h3.method,h4.method,h3.type,h4.type,h4.associatedconstant,h4.associatedtype{flex-basis:100%;font-weight:600;margin-top:16px;margin-bottom:10px;position:relative;}h3.impl,h3.method,h4.method.trait-impl,h3.type,h4.type.trait-impl,h4.associatedconstant.trait-impl,h4.associatedtype.trait-impl{padding-left:15px;}h1,h2,h3,h4,.sidebar,a.source,.search-input,.content table td:first-child>a,div.item-list .out-of-band,#source-sidebar,#sidebar-toggle,details.rustdoc-toggle>summary::before,details.undocumented>summary::before,.content ul.crate a.crate,#main>ul.docblock>li>a{font-family:"Fira Sans",Arial,sans-serif;}.content ul.crate a.crate{font-size:16px/1.6;}ol,ul{padding-left:25px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.6em;}p{margin:0 0 .6em 0;}summary{outline:none;}code,pre,a.test-arrow{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.1em;}.docblock pre code,.docblock-short pre code{padding:0;padding-right:1ex;}pre{padding:14px;}.source .content pre{padding:20px;}img{max-width:100%;}li{position:relative;}.source .content{margin-top:50px;max-width:none;overflow:visible;margin-left:0px;}nav.sub{font-size:16px;text-transform:uppercase;}.sidebar{width:200px;position:fixed;left:0;top:0;bottom:0;overflow:auto;}*{scrollbar-width:initial;}.sidebar{scrollbar-width:thin;}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;}.sidebar .block>ul>li{margin-right:-10px;}.content,nav{max-width:960px;}.hidden{display:none !important;}.logo-container{height:100px;width:100px;position:relative;margin:20px auto;display:block;margin-top:10px;}.logo-container>img{max-width:100px;max-height:100px;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);display:block;}.sidebar .location{border:1px solid;font-size:17px;margin:30px 10px 20px 10px;text-align:center;word-wrap:break-word;}.sidebar .version{font-size:15px;text-align:center;border-bottom:1px solid;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word;}.location:empty{border:none;}.location a:first-child{font-weight:500;}.block{padding:0;margin-bottom:14px;}.block h2,.block h3{margin-top:0;margin-bottom:8px;text-align:center;}.block ul,.block li{margin:0 10px;padding:0;list-style:none;}.block a{display:block;text-overflow:ellipsis;overflow:hidden;line-height:15px;padding:7px 5px;font-size:14px;font-weight:300;transition:border 500ms ease-out;}.sidebar-title{border-top:1px solid;border-bottom:1px solid;text-align:center;font-size:17px;margin-bottom:5px;}.sidebar-links{margin-bottom:15px;}.sidebar-links>a{padding-left:10px;width:100%;}.sidebar-menu{display:none;}.content{padding:15px 0;}.source .content pre.rust{white-space:pre;overflow:auto;padding-left:0;}.rustdoc .example-wrap{display:inline-flex;margin-bottom:10px;}.example-wrap{position:relative;width:100%;}.example-wrap>pre.line-number{overflow:initial;border:1px solid;padding:13px 8px;text-align:right;border-top-left-radius:5px;border-bottom-left-radius:5px;}.rustdoc:not(.source) .example-wrap>pre.rust{width:100%;overflow-x:auto;}.rustdoc .example-wrap>pre{margin:0;}#search{margin-left:230px;position:relative;}#results>table{width:100%;table-layout:fixed;}.content>.example-wrap pre.line-numbers{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}.line-numbers span{cursor:pointer;}.docblock-short{overflow-wrap:anywhere;}.docblock-short p{display:inline;}.docblock-short p{overflow:hidden;text-overflow:ellipsis;margin:0;}.docblock>:not(pre)>code,.docblock-short>:not(pre)>code{white-space:pre-wrap;}.docblock h1,.docblock h2,.docblock h3,.docblock h4,.docblock h5{border-bottom:1px solid;}#main>.docblock h1{font-size:1.3em;}#main>.docblock h2{font-size:1.15em;}#main>.docblock h3,#main>.docblock h4,#main>.docblock h5{font-size:1em;}.docblock h1{font-size:1em;}.docblock h2{font-size:0.95em;}.docblock h3,.docblock h4,.docblock h5{font-size:0.9em;}.docblock{margin-left:24px;position:relative;}.content .out-of-band{flex-grow:0;text-align:right;font-size:23px;margin:0px;padding:0 0 0 12px;font-weight:normal;}h3.impl>.out-of-band{font-size:21px;}h4.method>.out-of-band{font-size:19px;}h4>code,h3>code,.invisible>code{max-width:calc(100% - 41px);display:block;}.invisible{width:100%;display:inline-block;}.content .in-band{flex-grow:1;margin:0px;padding:0px;}.in-band>code{display:inline-block;}#main{position:relative;}#main>.since{top:inherit;font-family:"Fira Sans",Arial,sans-serif;}.content table:not(.table-display){border-spacing:0 5px;}.content td{vertical-align:top;}.content td:first-child{padding-right:20px;}.content td p:first-child{margin-top:0;}.content td h1,.content td h2{margin-left:0;font-size:1.1em;}.content tr:first-child td{border-top:0;}.docblock table{margin:.5em 0;width:calc(100% - 2px);border:1px dashed;}.docblock table td{padding:.5em;border:1px dashed;}.docblock table th{padding:.5em;text-align:left;border:1px solid;}.fields+table{margin-bottom:1em;}.content .item-list{list-style-type:none;padding:0;}.content .multi-column{-moz-column-count:5;-moz-column-gap:2.5em;-webkit-column-count:5;-webkit-column-gap:2.5em;column-count:5;column-gap:2.5em;}.content .multi-column li{width:100%;display:inline-block;}.content .method{font-size:1em;position:relative;}.content .method .where,.content .fn .where,.content .where.fmt-newline{display:block;font-size:0.8em;}.content .methods>div:not(.notable-traits):not(.methods){margin-left:40px;margin-bottom:15px;}.content .docblock>.impl-items{margin-left:20px;margin-top:-34px;}.content .docblock>.impl-items>h4{border-bottom:0;}.content .docblock>.impl-items .table-display{margin:0;}.content .docblock>.impl-items table td{padding:0;}.content .docblock>.impl-items .table-display,.impl-items table td{border:none;}.content .item-info code{font-size:90%;}.content .item-info{position:relative;margin-left:33px;margin-top:-13px;}.sub-variant>div>.item-info{margin-top:initial;}.content .item-info::before{content:'⬑';font-size:25px;position:absolute;top:-6px;left:-19px;}.content .impl-items .method,.content .impl-items>.type,.impl-items>.associatedconstant,.impl-items>.associatedtype,.content .impl-items details>summary>.type,.impl-items details>summary>.associatedconstant,.impl-items details>summary>.associatedtype{margin-left:20px;}.content .impl-items .docblock,.content .impl-items .item-info{margin-bottom:.6em;}.content .impl-items>.item-info{margin-left:40px;}.methods>.item-info,.content .impl-items>.item-info{margin-top:-8px;}.impl-items{flex-basis:100%;}#main>.item-info{margin-top:0;}nav:not(.sidebar){border-bottom:1px solid;padding-bottom:10px;margin-bottom:10px;}nav.main{padding:20px 0;text-align:center;}nav.main .current{border-top:1px solid;border-bottom:1px solid;}nav.main .separator{border:1px solid;display:inline-block;height:23px;margin:0 20px;}nav.sum{text-align:right;}nav.sub form{display:inline;}nav.sub,.content{margin-left:230px;}a{text-decoration:none;background:transparent;}.small-section-header:hover>.anchor{display:initial;}.in-band:hover>.anchor,.impl:hover>.anchor,.method.trait-impl:hover>.anchor,.type.trait-impl:hover>.anchor,.associatedconstant.trait-impl:hover>.anchor,.associatedtype.trait-impl:hover>.anchor{display:inline-block;position:absolute;}.anchor{display:none;position:absolute;left:-7px;}.anchor.field{left:-5px;}.small-section-header>.anchor{left:-28px;padding-right:10px;}.anchor:before{content:'\2002\00a7\2002';}.docblock a:not(.srclink):not(.test-arrow):hover,.docblock-short a:not(.srclink):not(.test-arrow):hover,.item-info a{text-decoration:underline;}.invisible>.srclink,h4>code+.srclink,h3>code+.srclink{position:absolute;top:0;right:0;font-size:17px;font-weight:normal;}.block a.current.crate{font-weight:500;}.search-container{position:relative;}.search-container>div{display:inline-flex;width:calc(100% - 63px);}#crate-search{min-width:115px;margin-top:5px;padding:6px;padding-right:19px;flex:none;border:0;border-right:0;border-radius:4px 0 0 4px;outline:none;cursor:pointer;border-right:1px solid;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;text-overflow:"";background-repeat:no-repeat;background-color:transparent;background-size:20px;background-position:calc(100% - 1px) 56%;}.search-container>.top-button{position:absolute;right:0;top:10px;}.search-input{-moz-box-sizing:border-box !important;box-sizing:border-box !important;outline:none;border:none;border-radius:1px;margin-top:5px;padding:10px 16px;font-size:17px;transition:border-color 300ms ease;transition:border-radius 300ms ease-in-out;transition:box-shadow 300ms ease-in-out;width:100%;}#crate-search+.search-input{border-radius:0 1px 1px 0;width:calc(100% - 32px);}.search-input:focus{border-radius:2px;border:0;outline:0;}.search-results .desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;display:block;}.search-results a{display:block;}.content .search-results td:first-child{padding-right:0;width:50%;}.content .search-results td:first-child a{padding-right:10px;}.content .search-results td:first-child a:after{clear:both;content:"";display:block;}.content .search-results td:first-child a span{float:left;}tr.result span.primitive::after{content:' (primitive type)';font-style:italic;}tr.result span.keyword::after{content:' (keyword)';font-style:italic;}body.blur>:not(#help){filter:blur(8px);-webkit-filter:blur(8px);opacity:.7;}#help{width:100%;height:100vh;position:fixed;top:0;left:0;display:flex;justify-content:center;align-items:center;}#help>div{flex:0 0 auto;box-shadow:0 0 6px rgba(0,0,0,.2);width:550px;height:auto;border:1px solid;}#help dt{float:left;clear:left;display:block;margin-right:0.5rem;}#help>div>span{text-align:center;display:block;margin:10px 0;font-size:18px;border-bottom:1px solid #ccc;padding-bottom:4px;margin-bottom:6px;}#help dd{margin:5px 35px;}#help .infos{padding-left:0;}#help h1,#help h2{margin-top:0;}#help>div div{width:50%;float:left;padding:0 20px 20px 17px;;}.stab{display:table;border-width:1px;border-style:solid;padding:3px;margin-bottom:5px;font-size:90%;}.stab p{display:inline;}.stab summary{display:list-item;}.stab .emoji{font-size:1.5em;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.module-item .stab,.import-item .stab{border-radius:3px;display:inline-block;font-size:80%;line-height:1.2;margin-bottom:0;margin-right:.3em;padding:2px;vertical-align:text-bottom;}.module-item.unstable,.import-item.unstable{opacity:0.65;}.since{font-weight:normal;font-size:initial;position:absolute;right:0;top:0;}.impl-items .since,.impl .since,.methods .since{flex-grow:0;padding-left:12px;padding-right:2px;position:initial;}.impl-items .srclink,.impl .srclink,.methods .srclink{flex-grow:0;font-size:17px;font-weight:normal;}.impl-items code,.impl code,.methods code{flex-grow:1;}.impl-items h4,h4.impl,h3.impl,.methods h3{display:flex;flex-basis:100%;font-size:16px;margin-bottom:12px;justify-content:space-between;}.variants_table{width:100%;}.variants_table tbody tr td:first-child{width:1%;}td.summary-column{width:100%;}.summary{padding-right:0px;}pre.rust .question-mark{font-weight:bold;}a.test-arrow{display:inline-block;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:130%;top:5px;right:5px;z-index:1;}a.test-arrow:hover{text-decoration:none;}.section-header:hover a:before{position:absolute;left:-25px;padding-right:10px;content:'\2002\00a7\2002';}.section-header:hover a{text-decoration:none;}.section-header a{color:inherit;}.code-attribute{font-weight:300;}.since+.srclink{display:table-cell;padding-left:10px;}.item-spacer{width:100%;height:12px;}.out-of-band>span.since{position:initial;font-size:20px;margin-right:5px;}.sub-variant,.sub-variant>h3{margin-top:0px !important;padding-top:1px;}#main>details>.sub-variant>h3{font-size:15px;margin-left:25px;margin-bottom:5px;}.sub-variant>div{margin-left:20px;margin-bottom:10px;}.sub-variant>div>span{display:block;position:relative;}.toggle-label{display:inline-block;margin-left:4px;margin-top:3px;}.docblock>.section-header:first-child{margin-left:15px;margin-top:0;}.docblock>.section-header:first-child:hover>a:before{left:-10px;}#main>.variant,#main>.structfield{display:block;}:target>code{opacity:1;}.information{position:absolute;left:-25px;margin-top:7px;z-index:1;}.tooltip{position:relative;display:inline-block;cursor:pointer;}.tooltip::after{display:none;text-align:center;padding:5px 3px 3px 3px;border-radius:6px;margin-left:5px;font-size:16px;}.tooltip.ignore::after{content:"This example is not tested";}.tooltip.compile_fail::after{content:"This example deliberately fails to compile";}.tooltip.should_panic::after{content:"This example panics";}.tooltip.edition::after{content:"This code runs with edition " attr(data-edition);}.tooltip::before{content:" ";position:absolute;top:50%;left:16px;margin-top:-5px;border-width:5px;border-style:solid;display:none;}.tooltip:hover::before,.tooltip:hover::after{display:inline;}.tooltip.compile_fail,.tooltip.should_panic,.tooltip.ignore{font-weight:bold;font-size:20px;}.notable-traits-tooltip{display:inline-block;cursor:pointer;}.notable-traits:hover .notable-traits-tooltiptext,.notable-traits .notable-traits-tooltiptext.force-tooltip{display:inline-block;}.notable-traits .notable-traits-tooltiptext{display:none;padding:5px 3px 3px 3px;border-radius:6px;margin-left:5px;z-index:10;font-size:16px;cursor:default;position:absolute;border:1px solid;}.notable-traits-tooltip::after{content:"\00a0\00a0\00a0";}.notable-traits .notable,.notable-traits .docblock{margin:0;}.notable-traits .docblock code.content{margin:0;padding:0;font-size:20px;}pre.rust.rust-example-rendered{position:relative;}pre.rust{tab-size:4;-moz-tab-size:4;}.search-failed{text-align:center;margin-top:20px;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#titles{height:35px;}#titles>button{float:left;width:33.3%;text-align:center;font-size:18px;cursor:pointer;border:0;border-top:2px solid;}#titles>button:not(:last-child){margin-right:1px;width:calc(33.3% - 1px);}#titles>button>div.count{display:inline-block;font-size:16px;}.notable-traits{cursor:pointer;z-index:2;margin-left:5px;}h4>.notable-traits{position:absolute;left:-44px;top:2px;}#all-types{text-align:center;border:1px solid;margin:0 10px;margin-bottom:10px;display:block;border-radius:7px;}#all-types>p{margin:5px 0;}#sidebar-toggle{position:fixed;top:30px;left:300px;z-index:10;padding:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;cursor:pointer;font-weight:bold;transition:left .5s;font-size:1.2em;border:1px solid;border-left:0;}#source-sidebar{position:fixed;top:0;bottom:0;left:0;width:300px;z-index:1;overflow:auto;transition:left .5s;border-right:1px solid;}#source-sidebar>.title{font-size:1.5em;text-align:center;border-bottom:1px solid;margin-bottom:6px;}.theme-picker{position:absolute;left:211px;top:19px;}.theme-picker button{outline:none;}#settings-menu,#help-button{position:absolute;top:10px;}#settings-menu{right:0;outline:none;}#theme-picker,#settings-menu,#help-button,#copy-path{padding:4px;width:27px;height:29px;border:1px solid;border-radius:3px;cursor:pointer;}#help-button{right:30px;font-family:"Fira Sans",Arial,sans-serif;text-align:center;font-size:17px;padding-top:2px;}#copy-path{margin-left:10px;padding:0;padding-left:2px;}#copy-path>img{margin-bottom:2px;}#theme-choices{display:none;position:absolute;left:0;top:28px;border:1px solid;border-radius:3px;z-index:1;cursor:pointer;}#theme-choices>button{border:none;width:100%;padding:4px 8px;text-align:center;background:rgba(0,0,0,0);}#theme-choices>button:not(:first-child){border-top:1px solid;}h3.notable{margin:0;margin-bottom:13px;font-size:19px;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px;border-radius:3px;box-shadow:inset 0 -1px 0;cursor:default;}.hidden-by-impl-hider,.hidden-by-usual-hider{display:none !important;}#implementations-list>h3>span.in-band{width:100%;}.table-display{width:100%;border:0;border-collapse:collapse;border-spacing:0;font-size:16px;}.table-display tr td:first-child{padding-right:0;}.table-display tr td:last-child{float:right;}.table-display .out-of-band{position:relative;font-size:19px;display:block;}#implementors-list>.impl-items .table-display .out-of-band{font-size:17px;}.table-display td:hover .anchor{display:block;top:2px;left:-5px;}#main>ul{padding-left:10px;}#main>ul>li{list-style:none;}.non-exhaustive{margin-bottom:1em;}div.children{padding-left:27px;display:none;}div.name{cursor:pointer;position:relative;margin-left:16px;}div.files>a{display:block;padding:0 3px;}div.files>a:hover,div.name:hover{background-color:#a14b4b;}div.name.expand+.children{display:block;}div.name::before{content:"\25B6";padding-left:4px;font-size:0.7em;position:absolute;left:-16px;top:4px;}div.name.expand::before{transform:rotate(90deg);left:-15px;top:2px;}details.rustdoc-toggle>summary.hideme{cursor:pointer;}details.rustdoc-toggle>summary,details.undocumented>summary{list-style:none;}details.rustdoc-toggle>summary::-webkit-details-marker,details.rustdoc-toggle>summary::marker,details.undocumented>summary::-webkit-details-marker,details.undocumented>summary::marker{display:none;}details.rustdoc-toggle>summary.hideme>span{margin-left:9px;}details.rustdoc-toggle>summary::before{content:"[+]";font-weight:300;font-size:0.8em;letter-spacing:1px;cursor:pointer;}details.rustdoc-toggle.top-doc>summary,details.rustdoc-toggle.top-doc>summary::before,details.rustdoc-toggle.non-exhaustive>summary,details.rustdoc-toggle.non-exhaustive>summary::before{font-family:'Fira Sans';font-size:16px;}details.non-exhaustive{margin-bottom:8px;}details.rustdoc-toggle>summary.hideme::before{position:relative;}details.rustdoc-toggle>summary:not(.hideme)::before{position:absolute;left:-23px;top:initial;}.impl-items>details.rustdoc-toggle>summary:not(.hideme)::before,.undocumented>details.rustdoc-toggle>summary:not(.hideme)::before{position:absolute;top:3px;left:-2px;}details.rustdoc-toggle[open] >summary.hideme{position:absolute;}details.rustdoc-toggle,details.undocumented{position:relative;}details.rustdoc-toggle[open] >summary.hideme>span{display:none;}details.rustdoc-toggle[open] >summary::before{content:"[−]";display:inline;}details.undocumented>summary::before{content:"[+] Show hidden undocumented items";cursor:pointer;font-size:16px;font-weight:300;}details.undocumented[open] >summary::before{content:"[−] Hide undocumented items";}@media (min-width:701px){.docblock>.information:first-child>.tooltip{margin-top:16px;}}@media (max-width:700px){body{padding-top:0px;}.rustdoc>.sidebar{height:45px;min-height:40px;margin:0;margin-left:-15px;padding:0 15px;position:static;z-index:11;}.sidebar>.location{float:right;margin:0px;margin-top:2px;padding:3px 10px 1px 10px;min-height:39px;background:inherit;text-align:left;font-size:24px;}.sidebar .location:empty{padding:0;}.sidebar .logo-container{width:35px;height:35px;margin-top:5px;margin-bottom:5px;float:left;margin-left:50px;}.sidebar .logo-container>img{max-width:35px;max-height:35px;}.sidebar-menu{position:fixed;z-index:10;font-size:2rem;cursor:pointer;width:45px;left:0;text-align:center;display:block;border-bottom:1px solid;border-right:1px solid;height:45px;}.rustdoc.source>.sidebar>.sidebar-menu{display:none;}.sidebar-elems{position:fixed;z-index:1;left:0;top:45px;bottom:0;overflow-y:auto;border-right:1px solid;display:none;}.sidebar>.block.version{border-bottom:none;margin-top:12px;margin-bottom:0;}nav.sub{width:calc(100% - 32px);float:right;}.content{margin-left:0px;}#main,#search{margin-top:45px;padding:0;}.content h4>.out-of-band{position:inherit;}#search{margin-left:0;}.content .impl-items .method,.content .impl-items>.type,.impl-items>.associatedconstant,.impl-items>.associatedtype{display:flex;}.anchor{display:none !important;}.theme-picker{left:10px;top:54px;z-index:1;}h4>.notable-traits{position:absolute;left:-22px;top:24px;}#titles>button>div.count{float:left;width:100%;}#titles{height:50px;}.sidebar.mobile{position:fixed;width:100%;margin-left:0;background-color:rgba(0,0,0,0);height:100%;}.sidebar.mobile>div.version{overflow:hidden;max-height:33px;}.sidebar{width:calc(100% + 30px);}.show-it{display:block;width:246px;}.show-it>.block.items{margin:8px 0;}.show-it>.block.items>ul{margin:0;}.show-it>.block.items>ul>li{text-align:center;margin:2px 0;}.show-it>.block.items>ul>li>a{font-size:21px;}#sidebar-filler{position:fixed;left:45px;width:calc(100% - 45px);top:0;height:45px;z-index:-1;border-bottom:1px solid;}#main>details.rustdoc-toggle>summary::before,#main>div>details.rustdoc-toggle>summary::before{left:-11px;}#all-types{margin:10px;}#sidebar-toggle{top:100px;width:30px;font-size:1.5rem;text-align:center;padding:0;}#source-sidebar{z-index:11;}#main>.line-numbers{margin-top:0;}.notable-traits .notable-traits-tooltiptext{left:0;top:100%;}#help-button{display:none;}.search-container>div{width:calc(100% - 32px);}}@media print{nav.sub,.content .out-of-band{display:none;}}@media (max-width:464px){#titles,#titles>button{height:73px;}#main,#search{margin-top:100px;}#main>table:not(.table-display) td{word-break:break-word;width:50%;}.search-container>div{display:block;width:calc(100% - 37px);}#crate-search{width:100%;border-radius:4px;border:0;}#crate-search+.search-input{width:calc(100% + 71px);margin-left:-36px;}#theme-picker,#settings-menu{padding:5px;width:31px;height:31px;}#theme-picker{margin-top:-2px;}#settings-menu{top:7px;}.docblock{margin-left:12px;}}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/search-index.js b/third_party/rust/rstest/v0_12/crate/docs/head/search-index.js
new file mode 100644
index 0000000..a3edbf7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/search-index.js
@@ -0,0 +1,4 @@
+var searchIndex = JSON.parse('{\
+"rstest":{"doc":"This crate will help you to write simpler tests by …","t":[23,23],"n":["fixture","rstest"],"q":["rstest",""],"d":["Define a fixture that you can use in all <code>rstest</code>’s test …","The attribute that you should use for your tests. Your …"],"i":[0,0],"f":[null,null],"p":[]}\
+}');
+if (window.initSearch) {window.initSearch(searchIndex)};
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/settings.css b/third_party/rust/rstest/v0_12/crate/docs/head/settings.css
new file mode 100644
index 0000000..6709865
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/settings.css
@@ -0,0 +1 @@
+.setting-line{padding:5px;position:relative;}.setting-line>div{display:inline-block;vertical-align:top;font-size:17px;padding-top:2px;}.setting-line>.title{font-size:19px;width:100%;max-width:none;border-bottom:1px solid;}.toggle{position:relative;display:inline-block;width:45px;height:27px;margin-right:20px;}.toggle input{opacity:0;position:absolute;}.select-wrapper{float:right;position:relative;height:27px;min-width:25%;}.select-wrapper select{appearance:none;-moz-appearance:none;-webkit-appearance:none;background:none;border:2px solid #ccc;padding-right:28px;width:100%;}.select-wrapper img{pointer-events:none;position:absolute;right:0;bottom:0;background:#ccc;height:100%;width:28px;padding:0px 4px;}.select-wrapper select option{color:initial;}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.3s;transition:.3s;}.slider:before{position:absolute;content:"";height:19px;width:19px;left:4px;bottom:4px;background-color:white;-webkit-transition:.3s;transition:.3s;}input:checked+.slider{background-color:#2196F3;}input:focus+.slider{box-shadow:0 0 0 2px #0a84ff,0 0 0 6px rgba(10,132,255,0.3);}input:checked+.slider:before{-webkit-transform:translateX(19px);-ms-transform:translateX(19px);transform:translateX(19px);}.setting-line>.sub-settings{padding-left:42px;width:100%;display:block;}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/settings.html b/third_party/rust/rstest/v0_12/crate/docs/head/settings.html
new file mode 100644
index 0000000..4249411
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/settings.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Settings of Rustdoc"><meta name="keywords" content="rust, rustlang, rust-lang"><title>Rustdoc settings</title><link rel="stylesheet" type="text/css" href="./normalize.css"><link rel="stylesheet" type="text/css" href="./rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="./light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="./dark.css" disabled ><link rel="stylesheet" type="text/css" href="./ayu.css" disabled ><link rel="stylesheet" type="text/css" href="./settings.css"  ><script id="default-settings"></script><script src="./storage.js"></script><script src="./crates.js"></script><noscript><link rel="stylesheet" href="./noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="./favicon.svg">
+<link rel="alternate icon" type="image/png" href="./favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="./favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("./down-arrow.svg");}</style></head><body class="rustdoc mod"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='./rstest/index.html'><div class='logo-container rust-logo'><img src='./rust-logo.png' alt='logo'></div></a><p class="location">Settings</p><div class="sidebar-elems"></div></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="./brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="./settings.html"><img src="./wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><h1 class="fqn"><span class="in-band">Rustdoc settings</span></h1><div class="settings"><div class="setting-line"><div class="title">Theme preferences</div><div class="sub-settings"><div class="setting-line"><label class="toggle"><input type="checkbox" id="use-system-theme"  checked><span class="slider"></span></label><div>Use system theme</div></div><div class="setting-line"><div>Preferred dark theme</div><label class="select-wrapper"><select id="preferred-dark-theme" autocomplete="off"><option value="light" >light</option><option value="dark" selected>dark</option><option value="ayu" >ayu</option></select><img src="./down-arrow.svg" alt="Select item"></label></div><div class="setting-line"><div>Preferred light theme</div><label class="select-wrapper"><select id="preferred-light-theme" autocomplete="off"><option value="light" selected>light</option><option value="dark" >dark</option><option value="ayu" >ayu</option></select><img src="./down-arrow.svg" alt="Select item"></label></div></div>
+                 </div><div class="setting-line"><label class="toggle"><input type="checkbox" id="auto-hide-large-items"  checked><span class="slider"></span></label><div>Auto-hide item contents for large items.</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="auto-hide-method-docs" ><span class="slider"></span></label><div>Auto-hide item methods' documentation</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="auto-hide-trait-implementations"  checked><span class="slider"></span></label><div>Auto-hide trait implementation documentation</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="auto-collapse-implementors"  checked><span class="slider"></span></label><div>Auto-hide implementors of a trait</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="go-to-only-result" ><span class="slider"></span></label><div>Directly go to item in search if there is only one result</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="line-numbers" ><span class="slider"></span></label><div>Show line numbers on code examples</div></div><div class="setting-line"><label class="toggle"><input type="checkbox" id="disable-shortcuts" ><span class="slider"></span></label><div>Disable keyboard shortcuts</div></div></div><script src="./settings.js"></script></section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="./" data-current-crate="rstest" data-search-index-js="./search-index.js" data-search-js="./search.js"></div>
+    <script src="./main.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/settings.js b/third_party/rust/rstest/v0_12/crate/docs/head/settings.js
new file mode 100644
index 0000000..b4d6fdc
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/settings.js
@@ -0,0 +1 @@
+(function(){function changeSetting(settingName,value){updateLocalStorage("rustdoc-"+settingName,value);switch(settingName){case"preferred-dark-theme":case"preferred-light-theme":case"use-system-theme":updateSystemTheme();break}}function handleKey(ev){if(ev.ctrlKey||ev.altKey||ev.metaKey){return}switch(getVirtualKey(ev)){case"Enter":case"Return":case"Space":ev.target.checked=!ev.target.checked;ev.preventDefault();break}}function setEvents(){onEachLazy(document.getElementsByClassName("slider"),function(elem){var toggle=elem.previousElementSibling;var settingId=toggle.id;var settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=function(){changeSetting(this.id,this.checked)};toggle.onkeyup=handleKey;toggle.onkeyrelease=handleKey});onEachLazy(document.getElementsByClassName("select-wrapper"),function(elem){var select=elem.getElementsByTagName("select")[0];var settingId=select.id;var settingValue=getSettingValue(settingId);if(settingValue!==null){select.value=settingValue}select.onchange=function(){changeSetting(this.id,this.value)}})}window.addEventListener("DOMContentLoaded",setEvents)})()
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/source-files.js b/third_party/rust/rstest/v0_12/crate/docs/head/source-files.js
new file mode 100644
index 0000000..c6b895b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/source-files.js
@@ -0,0 +1,3 @@
+var N = null;var sourcesIndex = {};
+sourcesIndex["rstest"] = {"name":"","dirs":[{"name":"parse","files":["expressions.rs","fixture.rs","future.rs","macros.rs","mod.rs","rstest.rs","testcase.rs","vlist.rs"]},{"name":"render","files":["fixture.rs","inject.rs","mod.rs","wrapper.rs"]}],"files":["error.rs","lib.rs","refident.rs","resolver.rs","utils.rs"]};
+createSourceSidebar();
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/source-script.js b/third_party/rust/rstest/v0_12/crate/docs/head/source-script.js
new file mode 100644
index 0000000..5dc8fee
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/source-script.js
@@ -0,0 +1 @@
+(function(){function getCurrentFilePath(){var parts=window.location.pathname.split("/");var rootPathParts=window.rootPath.split("/");for(var i=0,len=rootPathParts.length;i<len;++i){if(rootPathParts[i]===".."){parts.pop()}}var file=window.location.pathname.substring(parts.join("/").length);if(file.startsWith("/")){file=file.substring(1)}return file.substring(0,file.length-5)}function createDirEntry(elem,parent,fullPath,currentFile,hasFoundFile){var name=document.createElement("div");name.className="name";fullPath+=elem["name"]+"/";name.onclick=function(){if(hasClass(this,"expand")){removeClass(this,"expand")}else{addClass(this,"expand")}};name.innerText=elem["name"];var i,len;var children=document.createElement("div");children.className="children";var folders=document.createElement("div");folders.className="folders";if(elem.dirs){for(i=0,len=elem.dirs.length;i<len;++i){if(createDirEntry(elem.dirs[i],folders,fullPath,currentFile,hasFoundFile)){addClass(name,"expand");hasFoundFile=true}}}children.appendChild(folders);var files=document.createElement("div");files.className="files";if(elem.files){for(i=0,len=elem.files.length;i<len;++i){var file=document.createElement("a");file.innerText=elem.files[i];file.href=window.rootPath+"src/"+fullPath+elem.files[i]+".html";if(!hasFoundFile&&currentFile===fullPath+elem.files[i]){file.className="selected";addClass(name,"expand");hasFoundFile=true}files.appendChild(file)}}search.fullPath=fullPath;children.appendChild(files);parent.appendChild(name);parent.appendChild(children);return hasFoundFile&&currentFile.startsWith(fullPath)}function toggleSidebar(){var sidebar=document.getElementById("source-sidebar");var child=this.children[0].children[0];if(child.innerText===">"){sidebar.style.left="";this.style.left="";child.innerText="<";updateLocalStorage("rustdoc-source-sidebar-show","true")}else{sidebar.style.left="-300px";this.style.left="0";child.innerText=">";updateLocalStorage("rustdoc-source-sidebar-show","false")}}function createSidebarToggle(){var sidebarToggle=document.createElement("div");sidebarToggle.id="sidebar-toggle";sidebarToggle.onclick=toggleSidebar;var inner1=document.createElement("div");inner1.style.position="relative";var inner2=document.createElement("div");inner2.style.paddingTop="3px";if(getCurrentValue("rustdoc-source-sidebar-show")==="true"){inner2.innerText="<"}else{inner2.innerText=">";sidebarToggle.style.left="0"}inner1.appendChild(inner2);sidebarToggle.appendChild(inner1);return sidebarToggle}function createSourceSidebar(){if(!window.rootPath.endsWith("/")){window.rootPath+="/"}var main=document.getElementById("main");var sidebarToggle=createSidebarToggle();main.insertBefore(sidebarToggle,main.firstChild);var sidebar=document.createElement("div");sidebar.id="source-sidebar";if(getCurrentValue("rustdoc-source-sidebar-show")!=="true"){sidebar.style.left="-300px"}var currentFile=getCurrentFilePath();var hasFoundFile=false;var title=document.createElement("div");title.className="title";title.innerText="Files";sidebar.appendChild(title);Object.keys(sourcesIndex).forEach(function(key){sourcesIndex[key].name=key;hasFoundFile=createDirEntry(sourcesIndex[key],sidebar,"",currentFile,hasFoundFile)});main.insertBefore(sidebar,main.firstChild);var selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}var lineNumbersRegex=/^#?(\d+)(?:-(\d+))?$/;function highlightSourceLines(scrollTo,match){if(typeof match==="undefined"){match=window.location.hash.match(lineNumbersRegex)}if(!match){return}var from=parseInt(match[1],10);var to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to<from){var tmp=to;to=from;from=tmp}var elem=document.getElementById(from);if(!elem){return}if(scrollTo){var x=document.getElementById(from);if(x){x.scrollIntoView()}}onEachLazy(document.getElementsByClassName("line-numbers"),function(e){onEachLazy(e.getElementsByTagName("span"),function(i_e){removeClass(i_e,"line-highlighted")})});for(var i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}var handleSourceHighlight=(function(){var prev_line_id=0;var set_fragment=function(name){var x=window.scrollX,y=window.scrollY;if(searchState.browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSourceLines(true)}else{location.replace("#"+name)}window.scrollTo(x,y)};return function(ev){var cur_line_id=parseInt(ev.target.id,10);ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){var tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",function(){var match=window.location.hash.match(lineNumbersRegex);if(match){return highlightSourceLines(false,match)}});onEachLazy(document.getElementsByClassName("line-numbers"),function(el){el.addEventListener("click",handleSourceHighlight)});highlightSourceLines(true);window.createSourceSidebar=createSourceSidebar})()
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/error.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/error.rs.html
new file mode 100644
index 0000000..513d7e4
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/error.rs.html
@@ -0,0 +1,367 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/error.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>error.rs - source</title><link rel="stylesheet" type="text/css" href="../../normalize.css"><link rel="stylesheet" type="text/css" href="../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../ayu.css" disabled ><script id="default-settings"></script><script src="../../storage.js"></script><script src="../../crates.js"></script><noscript><link rel="stylesheet" href="../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../settings.html"><img src="../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+</pre><pre class="rust">
+<span class="doccomment">/// Module for error rendering stuff</span>
+<span class="kw">use</span> <span class="ident">std::collections::HashMap</span>;
+
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">syn::spanned::Spanned</span>;
+<span class="kw">use</span> <span class="ident">syn::ItemFn</span>;
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse</span>::{
+    <span class="ident">fixture::FixtureInfo</span>,
+    <span class="ident">rstest</span>::{<span class="ident">RsTestData</span>, <span class="ident">RsTestInfo</span>},
+};
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::refident::MaybeIdent</span>;
+
+<span class="kw">use</span> <span class="kw">super</span><span class="ident">::utils::fn_args_has_ident</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">rstest</span>(<span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="ident">missed_arguments</span>(<span class="ident">test</span>, <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">items</span>.<span class="ident">iter</span>())
+        .<span class="ident">chain</span>(<span class="ident">duplicate_arguments</span>(<span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">items</span>.<span class="ident">iter</span>()))
+        .<span class="ident">chain</span>(<span class="ident">invalid_cases</span>(<span class="kw-2">&amp;</span><span class="ident">info</span>.<span class="ident">data</span>))
+        .<span class="ident">chain</span>(<span class="ident">case_args_without_cases</span>(<span class="kw-2">&amp;</span><span class="ident">info</span>.<span class="ident">data</span>))
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">e</span><span class="op">|</span> <span class="ident">e</span>.<span class="ident">to_compile_error</span>())
+        .<span class="ident">collect</span>()
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fixture</span>(<span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="kw-2">&amp;</span><span class="ident">FixtureInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="ident">missed_arguments</span>(<span class="ident">test</span>, <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">items</span>.<span class="ident">iter</span>())
+        .<span class="ident">chain</span>(<span class="ident">duplicate_arguments</span>(<span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">items</span>.<span class="ident">iter</span>()))
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">e</span><span class="op">|</span> <span class="ident">e</span>.<span class="ident">to_compile_error</span>())
+        .<span class="ident">collect</span>()
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Debug</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">ErrorsVec</span>(<span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>);
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">_merge_errors</span><span class="op">&lt;</span><span class="ident">R1</span>, <span class="ident">R2</span><span class="op">&gt;</span>(
+    <span class="ident">r1</span>: <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">R1</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>,
+    <span class="ident">r2</span>: <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">R2</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span>(<span class="ident">R1</span>, <span class="ident">R2</span>), <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">match</span> (<span class="ident">r1</span>, <span class="ident">r2</span>) {
+        (<span class="prelude-val">Ok</span>(<span class="ident">r1</span>), <span class="prelude-val">Ok</span>(<span class="ident">r2</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Ok</span>((<span class="ident">r1</span>, <span class="ident">r2</span>)),
+        (<span class="prelude-val">Ok</span>(<span class="kw">_</span>), <span class="prelude-val">Err</span>(<span class="ident">e</span>)) <span class="op">|</span> (<span class="prelude-val">Err</span>(<span class="ident">e</span>), <span class="prelude-val">Ok</span>(<span class="kw">_</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Err</span>(<span class="ident">e</span>),
+        (<span class="prelude-val">Err</span>(<span class="kw-2">mut</span> <span class="ident">e1</span>), <span class="prelude-val">Err</span>(<span class="kw-2">mut</span> <span class="ident">e2</span>)) <span class="op">=</span><span class="op">&gt;</span> {
+            <span class="ident">e1</span>.<span class="ident">append</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">e2</span>);
+            <span class="prelude-val">Err</span>(<span class="ident">e1</span>)
+        }
+    }
+}
+
+<span class="macro">macro_rules!</span> <span class="ident">merge_errors</span> {
+    (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">e</span>:<span class="ident">expr</span>) <span class="op">=</span><span class="op">&gt;</span> {
+        <span class="macro-nonterminal">$</span><span class="macro-nonterminal">e</span>
+    };
+    (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">e</span>:<span class="ident">expr</span>, $(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">es</span>:<span class="ident">expr</span>), <span class="op">+</span>) <span class="op">=</span><span class="op">&gt;</span> {
+        <span class="kw">crate</span><span class="ident">::error::_merge_errors</span>(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">e</span>, <span class="macro">merge_errors!</span>($(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">es</span>),<span class="kw-2">*</span>));
+    };
+}
+
+<span class="macro">macro_rules!</span> <span class="ident">composed_tuple</span> {
+    (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">i</span>:<span class="ident">ident</span>) <span class="op">=</span><span class="op">&gt;</span> {
+        <span class="macro-nonterminal">$</span><span class="macro-nonterminal">i</span>
+    };
+    (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">i</span>:<span class="ident">ident</span>, $(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">is</span>:<span class="ident">ident</span>), <span class="op">+</span>) <span class="op">=</span><span class="op">&gt;</span> {
+        (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">i</span>, <span class="macro">composed_tuple!</span>($(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">is</span>),<span class="kw-2">*</span>))
+    };
+}
+
+<span class="kw">impl</span> <span class="ident">std::ops::Deref</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">type</span> <span class="ident">Target</span> <span class="op">=</span> <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>;
+    <span class="kw">fn</span> <span class="ident">deref</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="self">Self</span><span class="ident">::Target</span> {
+        <span class="kw-2">&amp;</span><span class="self">self</span>.<span class="number">0</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">std::ops::DerefMut</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">deref_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">Self</span><span class="ident">::Target</span> {
+        <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>.<span class="number">0</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">errors</span>: <span class="ident">syn::Error</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="macro">vec!</span>[<span class="ident">errors</span>].<span class="ident">into</span>()
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">errors</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span>(<span class="ident">errors</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Into</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">into</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">quote::ToTokens</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="ident">tokens</span>.<span class="ident">extend</span>(<span class="self">self</span>.<span class="number">0</span>.<span class="ident">iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">e</span><span class="op">|</span> <span class="ident">e</span>.<span class="ident">to_compile_error</span>()))
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Into</span><span class="op">&lt;</span><span class="ident">proc_macro::TokenStream</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">ErrorsVec</span> {
+    <span class="kw">fn</span> <span class="ident">into</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">proc_macro::TokenStream</span> {
+        <span class="kw">use</span> <span class="ident">quote::ToTokens</span>;
+        <span class="self">self</span>.<span class="ident">into_token_stream</span>().<span class="ident">into</span>()
+    }
+}
+
+<span class="kw">type</span> <span class="ident">Errors</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> <span class="op">=</span> <span class="ident">Box</span><span class="op">&lt;</span><span class="kw">dyn</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="ident">syn::Error</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span>;
+
+<span class="kw">fn</span> <span class="ident">missed_arguments</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span>, <span class="ident">I</span>: <span class="ident">MaybeIdent</span> <span class="op">+</span> <span class="ident">Spanned</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">ItemFn</span>,
+    <span class="ident">args</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">I</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Errors</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> {
+    <span class="ident">Box::new</span>(
+        <span class="ident">args</span>.<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="ident">it</span>.<span class="ident">maybe_ident</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">ident</span><span class="op">|</span> (<span class="ident">it</span>, <span class="ident">ident</span>)))
+            .<span class="ident">filter</span>(<span class="kw">move</span> <span class="op">|</span>(<span class="kw">_</span>, <span class="ident">ident</span>)<span class="op">|</span> <span class="op">!</span><span class="ident">fn_args_has_ident</span>(<span class="ident">test</span>, <span class="ident">ident</span>))
+            .<span class="ident">map</span>(<span class="op">|</span>(<span class="ident">missed</span>, <span class="ident">ident</span>)<span class="op">|</span> {
+                <span class="ident">syn::Error::new</span>(
+                    <span class="ident">missed</span>.<span class="ident">span</span>(),
+                    <span class="kw-2">&amp;</span><span class="macro">format!</span>(
+                        <span class="string">&quot;Missed argument: &#39;{}&#39; should be a test function argument.&quot;</span>,
+                        <span class="ident">ident</span>
+                    ),
+                )
+            }),
+    )
+}
+
+<span class="kw">fn</span> <span class="ident">duplicate_arguments</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span>, <span class="ident">I</span>: <span class="ident">MaybeIdent</span> <span class="op">+</span> <span class="ident">Spanned</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">args</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">I</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Errors</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">used</span> <span class="op">=</span> <span class="ident">HashMap::new</span>();
+    <span class="ident">Box::new</span>(
+        <span class="ident">args</span>.<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="ident">it</span>.<span class="ident">maybe_ident</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">ident</span><span class="op">|</span> (<span class="ident">it</span>, <span class="ident">ident</span>)))
+            .<span class="ident">filter_map</span>(<span class="kw">move</span> <span class="op">|</span>(<span class="ident">it</span>, <span class="ident">ident</span>)<span class="op">|</span> {
+                <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="ident">ident</span>.<span class="ident">to_string</span>();
+                <span class="kw">let</span> <span class="ident">is_duplicate</span> <span class="op">=</span> <span class="ident">used</span>.<span class="ident">contains_key</span>(<span class="kw-2">&amp;</span><span class="ident">name</span>);
+                <span class="ident">used</span>.<span class="ident">insert</span>(<span class="ident">name</span>, <span class="ident">it</span>);
+                <span class="kw">match</span> <span class="ident">is_duplicate</span> {
+                    <span class="bool-val">true</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>((<span class="ident">it</span>, <span class="ident">ident</span>)),
+                    <span class="bool-val">false</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+                }
+            })
+            .<span class="ident">map</span>(<span class="op">|</span>(<span class="ident">duplicate</span>, <span class="ident">ident</span>)<span class="op">|</span> {
+                <span class="ident">syn::Error::new</span>(
+                    <span class="ident">duplicate</span>.<span class="ident">span</span>(),
+                    <span class="kw-2">&amp;</span><span class="macro">format!</span>(<span class="string">&quot;Duplicate argument: &#39;{}&#39; is already defined.&quot;</span>, <span class="ident">ident</span>),
+                )
+            }),
+    )
+}
+
+<span class="kw">fn</span> <span class="ident">invalid_cases</span>(<span class="ident">params</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestData</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Errors</span> {
+    <span class="kw">let</span> <span class="ident">n_args</span> <span class="op">=</span> <span class="ident">params</span>.<span class="ident">case_args</span>().<span class="ident">count</span>();
+    <span class="ident">Box::new</span>(
+        <span class="ident">params</span>
+            .<span class="ident">cases</span>()
+            .<span class="ident">filter</span>(<span class="kw">move</span> <span class="op">|</span><span class="ident">case</span><span class="op">|</span> <span class="ident">case</span>.<span class="ident">args</span>.<span class="ident">len</span>() <span class="op">!</span><span class="op">=</span> <span class="ident">n_args</span>)
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">case</span><span class="op">|</span> {
+                <span class="ident">syn::Error::new_spanned</span>(
+                    <span class="kw-2">&amp;</span><span class="ident">case</span>,
+                    <span class="string">&quot;Wrong case signature: should match the given parameters list.&quot;</span>,
+                )
+            }),
+    )
+}
+
+<span class="kw">fn</span> <span class="ident">case_args_without_cases</span>(<span class="ident">params</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestData</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Errors</span> {
+    <span class="kw">if</span> <span class="op">!</span><span class="ident">params</span>.<span class="ident">has_cases</span>() {
+        <span class="kw">return</span> <span class="ident">Box::new</span>(
+            <span class="ident">params</span>
+                .<span class="ident">case_args</span>()
+                .<span class="ident">map</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">syn::Error::new</span>(<span class="ident">a</span>.<span class="ident">span</span>(), <span class="string">&quot;No cases for this argument.&quot;</span>)),
+        );
+    }
+    <span class="ident">Box::new</span>(<span class="ident">std::iter::empty</span>())
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../" data-current-crate="rstest" data-search-index-js="../../search-index.js" data-search-js="../../search.js"></div>
+    <script src="../../main.js"></script><script src="../../source-script.js"></script><script src="../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/lib.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/lib.rs.html
new file mode 100644
index 0000000..cb467ea7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/lib.rs.html
@@ -0,0 +1,2387 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/lib.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>lib.rs - source</title><link rel="stylesheet" type="text/css" href="../../normalize.css"><link rel="stylesheet" type="text/css" href="../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../ayu.css" disabled ><script id="default-settings"></script><script src="../../storage.js"></script><script src="../../crates.js"></script><noscript><link rel="stylesheet" href="../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../settings.html"><img src="../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">   1</span>
+<span id="2">   2</span>
+<span id="3">   3</span>
+<span id="4">   4</span>
+<span id="5">   5</span>
+<span id="6">   6</span>
+<span id="7">   7</span>
+<span id="8">   8</span>
+<span id="9">   9</span>
+<span id="10">  10</span>
+<span id="11">  11</span>
+<span id="12">  12</span>
+<span id="13">  13</span>
+<span id="14">  14</span>
+<span id="15">  15</span>
+<span id="16">  16</span>
+<span id="17">  17</span>
+<span id="18">  18</span>
+<span id="19">  19</span>
+<span id="20">  20</span>
+<span id="21">  21</span>
+<span id="22">  22</span>
+<span id="23">  23</span>
+<span id="24">  24</span>
+<span id="25">  25</span>
+<span id="26">  26</span>
+<span id="27">  27</span>
+<span id="28">  28</span>
+<span id="29">  29</span>
+<span id="30">  30</span>
+<span id="31">  31</span>
+<span id="32">  32</span>
+<span id="33">  33</span>
+<span id="34">  34</span>
+<span id="35">  35</span>
+<span id="36">  36</span>
+<span id="37">  37</span>
+<span id="38">  38</span>
+<span id="39">  39</span>
+<span id="40">  40</span>
+<span id="41">  41</span>
+<span id="42">  42</span>
+<span id="43">  43</span>
+<span id="44">  44</span>
+<span id="45">  45</span>
+<span id="46">  46</span>
+<span id="47">  47</span>
+<span id="48">  48</span>
+<span id="49">  49</span>
+<span id="50">  50</span>
+<span id="51">  51</span>
+<span id="52">  52</span>
+<span id="53">  53</span>
+<span id="54">  54</span>
+<span id="55">  55</span>
+<span id="56">  56</span>
+<span id="57">  57</span>
+<span id="58">  58</span>
+<span id="59">  59</span>
+<span id="60">  60</span>
+<span id="61">  61</span>
+<span id="62">  62</span>
+<span id="63">  63</span>
+<span id="64">  64</span>
+<span id="65">  65</span>
+<span id="66">  66</span>
+<span id="67">  67</span>
+<span id="68">  68</span>
+<span id="69">  69</span>
+<span id="70">  70</span>
+<span id="71">  71</span>
+<span id="72">  72</span>
+<span id="73">  73</span>
+<span id="74">  74</span>
+<span id="75">  75</span>
+<span id="76">  76</span>
+<span id="77">  77</span>
+<span id="78">  78</span>
+<span id="79">  79</span>
+<span id="80">  80</span>
+<span id="81">  81</span>
+<span id="82">  82</span>
+<span id="83">  83</span>
+<span id="84">  84</span>
+<span id="85">  85</span>
+<span id="86">  86</span>
+<span id="87">  87</span>
+<span id="88">  88</span>
+<span id="89">  89</span>
+<span id="90">  90</span>
+<span id="91">  91</span>
+<span id="92">  92</span>
+<span id="93">  93</span>
+<span id="94">  94</span>
+<span id="95">  95</span>
+<span id="96">  96</span>
+<span id="97">  97</span>
+<span id="98">  98</span>
+<span id="99">  99</span>
+<span id="100"> 100</span>
+<span id="101"> 101</span>
+<span id="102"> 102</span>
+<span id="103"> 103</span>
+<span id="104"> 104</span>
+<span id="105"> 105</span>
+<span id="106"> 106</span>
+<span id="107"> 107</span>
+<span id="108"> 108</span>
+<span id="109"> 109</span>
+<span id="110"> 110</span>
+<span id="111"> 111</span>
+<span id="112"> 112</span>
+<span id="113"> 113</span>
+<span id="114"> 114</span>
+<span id="115"> 115</span>
+<span id="116"> 116</span>
+<span id="117"> 117</span>
+<span id="118"> 118</span>
+<span id="119"> 119</span>
+<span id="120"> 120</span>
+<span id="121"> 121</span>
+<span id="122"> 122</span>
+<span id="123"> 123</span>
+<span id="124"> 124</span>
+<span id="125"> 125</span>
+<span id="126"> 126</span>
+<span id="127"> 127</span>
+<span id="128"> 128</span>
+<span id="129"> 129</span>
+<span id="130"> 130</span>
+<span id="131"> 131</span>
+<span id="132"> 132</span>
+<span id="133"> 133</span>
+<span id="134"> 134</span>
+<span id="135"> 135</span>
+<span id="136"> 136</span>
+<span id="137"> 137</span>
+<span id="138"> 138</span>
+<span id="139"> 139</span>
+<span id="140"> 140</span>
+<span id="141"> 141</span>
+<span id="142"> 142</span>
+<span id="143"> 143</span>
+<span id="144"> 144</span>
+<span id="145"> 145</span>
+<span id="146"> 146</span>
+<span id="147"> 147</span>
+<span id="148"> 148</span>
+<span id="149"> 149</span>
+<span id="150"> 150</span>
+<span id="151"> 151</span>
+<span id="152"> 152</span>
+<span id="153"> 153</span>
+<span id="154"> 154</span>
+<span id="155"> 155</span>
+<span id="156"> 156</span>
+<span id="157"> 157</span>
+<span id="158"> 158</span>
+<span id="159"> 159</span>
+<span id="160"> 160</span>
+<span id="161"> 161</span>
+<span id="162"> 162</span>
+<span id="163"> 163</span>
+<span id="164"> 164</span>
+<span id="165"> 165</span>
+<span id="166"> 166</span>
+<span id="167"> 167</span>
+<span id="168"> 168</span>
+<span id="169"> 169</span>
+<span id="170"> 170</span>
+<span id="171"> 171</span>
+<span id="172"> 172</span>
+<span id="173"> 173</span>
+<span id="174"> 174</span>
+<span id="175"> 175</span>
+<span id="176"> 176</span>
+<span id="177"> 177</span>
+<span id="178"> 178</span>
+<span id="179"> 179</span>
+<span id="180"> 180</span>
+<span id="181"> 181</span>
+<span id="182"> 182</span>
+<span id="183"> 183</span>
+<span id="184"> 184</span>
+<span id="185"> 185</span>
+<span id="186"> 186</span>
+<span id="187"> 187</span>
+<span id="188"> 188</span>
+<span id="189"> 189</span>
+<span id="190"> 190</span>
+<span id="191"> 191</span>
+<span id="192"> 192</span>
+<span id="193"> 193</span>
+<span id="194"> 194</span>
+<span id="195"> 195</span>
+<span id="196"> 196</span>
+<span id="197"> 197</span>
+<span id="198"> 198</span>
+<span id="199"> 199</span>
+<span id="200"> 200</span>
+<span id="201"> 201</span>
+<span id="202"> 202</span>
+<span id="203"> 203</span>
+<span id="204"> 204</span>
+<span id="205"> 205</span>
+<span id="206"> 206</span>
+<span id="207"> 207</span>
+<span id="208"> 208</span>
+<span id="209"> 209</span>
+<span id="210"> 210</span>
+<span id="211"> 211</span>
+<span id="212"> 212</span>
+<span id="213"> 213</span>
+<span id="214"> 214</span>
+<span id="215"> 215</span>
+<span id="216"> 216</span>
+<span id="217"> 217</span>
+<span id="218"> 218</span>
+<span id="219"> 219</span>
+<span id="220"> 220</span>
+<span id="221"> 221</span>
+<span id="222"> 222</span>
+<span id="223"> 223</span>
+<span id="224"> 224</span>
+<span id="225"> 225</span>
+<span id="226"> 226</span>
+<span id="227"> 227</span>
+<span id="228"> 228</span>
+<span id="229"> 229</span>
+<span id="230"> 230</span>
+<span id="231"> 231</span>
+<span id="232"> 232</span>
+<span id="233"> 233</span>
+<span id="234"> 234</span>
+<span id="235"> 235</span>
+<span id="236"> 236</span>
+<span id="237"> 237</span>
+<span id="238"> 238</span>
+<span id="239"> 239</span>
+<span id="240"> 240</span>
+<span id="241"> 241</span>
+<span id="242"> 242</span>
+<span id="243"> 243</span>
+<span id="244"> 244</span>
+<span id="245"> 245</span>
+<span id="246"> 246</span>
+<span id="247"> 247</span>
+<span id="248"> 248</span>
+<span id="249"> 249</span>
+<span id="250"> 250</span>
+<span id="251"> 251</span>
+<span id="252"> 252</span>
+<span id="253"> 253</span>
+<span id="254"> 254</span>
+<span id="255"> 255</span>
+<span id="256"> 256</span>
+<span id="257"> 257</span>
+<span id="258"> 258</span>
+<span id="259"> 259</span>
+<span id="260"> 260</span>
+<span id="261"> 261</span>
+<span id="262"> 262</span>
+<span id="263"> 263</span>
+<span id="264"> 264</span>
+<span id="265"> 265</span>
+<span id="266"> 266</span>
+<span id="267"> 267</span>
+<span id="268"> 268</span>
+<span id="269"> 269</span>
+<span id="270"> 270</span>
+<span id="271"> 271</span>
+<span id="272"> 272</span>
+<span id="273"> 273</span>
+<span id="274"> 274</span>
+<span id="275"> 275</span>
+<span id="276"> 276</span>
+<span id="277"> 277</span>
+<span id="278"> 278</span>
+<span id="279"> 279</span>
+<span id="280"> 280</span>
+<span id="281"> 281</span>
+<span id="282"> 282</span>
+<span id="283"> 283</span>
+<span id="284"> 284</span>
+<span id="285"> 285</span>
+<span id="286"> 286</span>
+<span id="287"> 287</span>
+<span id="288"> 288</span>
+<span id="289"> 289</span>
+<span id="290"> 290</span>
+<span id="291"> 291</span>
+<span id="292"> 292</span>
+<span id="293"> 293</span>
+<span id="294"> 294</span>
+<span id="295"> 295</span>
+<span id="296"> 296</span>
+<span id="297"> 297</span>
+<span id="298"> 298</span>
+<span id="299"> 299</span>
+<span id="300"> 300</span>
+<span id="301"> 301</span>
+<span id="302"> 302</span>
+<span id="303"> 303</span>
+<span id="304"> 304</span>
+<span id="305"> 305</span>
+<span id="306"> 306</span>
+<span id="307"> 307</span>
+<span id="308"> 308</span>
+<span id="309"> 309</span>
+<span id="310"> 310</span>
+<span id="311"> 311</span>
+<span id="312"> 312</span>
+<span id="313"> 313</span>
+<span id="314"> 314</span>
+<span id="315"> 315</span>
+<span id="316"> 316</span>
+<span id="317"> 317</span>
+<span id="318"> 318</span>
+<span id="319"> 319</span>
+<span id="320"> 320</span>
+<span id="321"> 321</span>
+<span id="322"> 322</span>
+<span id="323"> 323</span>
+<span id="324"> 324</span>
+<span id="325"> 325</span>
+<span id="326"> 326</span>
+<span id="327"> 327</span>
+<span id="328"> 328</span>
+<span id="329"> 329</span>
+<span id="330"> 330</span>
+<span id="331"> 331</span>
+<span id="332"> 332</span>
+<span id="333"> 333</span>
+<span id="334"> 334</span>
+<span id="335"> 335</span>
+<span id="336"> 336</span>
+<span id="337"> 337</span>
+<span id="338"> 338</span>
+<span id="339"> 339</span>
+<span id="340"> 340</span>
+<span id="341"> 341</span>
+<span id="342"> 342</span>
+<span id="343"> 343</span>
+<span id="344"> 344</span>
+<span id="345"> 345</span>
+<span id="346"> 346</span>
+<span id="347"> 347</span>
+<span id="348"> 348</span>
+<span id="349"> 349</span>
+<span id="350"> 350</span>
+<span id="351"> 351</span>
+<span id="352"> 352</span>
+<span id="353"> 353</span>
+<span id="354"> 354</span>
+<span id="355"> 355</span>
+<span id="356"> 356</span>
+<span id="357"> 357</span>
+<span id="358"> 358</span>
+<span id="359"> 359</span>
+<span id="360"> 360</span>
+<span id="361"> 361</span>
+<span id="362"> 362</span>
+<span id="363"> 363</span>
+<span id="364"> 364</span>
+<span id="365"> 365</span>
+<span id="366"> 366</span>
+<span id="367"> 367</span>
+<span id="368"> 368</span>
+<span id="369"> 369</span>
+<span id="370"> 370</span>
+<span id="371"> 371</span>
+<span id="372"> 372</span>
+<span id="373"> 373</span>
+<span id="374"> 374</span>
+<span id="375"> 375</span>
+<span id="376"> 376</span>
+<span id="377"> 377</span>
+<span id="378"> 378</span>
+<span id="379"> 379</span>
+<span id="380"> 380</span>
+<span id="381"> 381</span>
+<span id="382"> 382</span>
+<span id="383"> 383</span>
+<span id="384"> 384</span>
+<span id="385"> 385</span>
+<span id="386"> 386</span>
+<span id="387"> 387</span>
+<span id="388"> 388</span>
+<span id="389"> 389</span>
+<span id="390"> 390</span>
+<span id="391"> 391</span>
+<span id="392"> 392</span>
+<span id="393"> 393</span>
+<span id="394"> 394</span>
+<span id="395"> 395</span>
+<span id="396"> 396</span>
+<span id="397"> 397</span>
+<span id="398"> 398</span>
+<span id="399"> 399</span>
+<span id="400"> 400</span>
+<span id="401"> 401</span>
+<span id="402"> 402</span>
+<span id="403"> 403</span>
+<span id="404"> 404</span>
+<span id="405"> 405</span>
+<span id="406"> 406</span>
+<span id="407"> 407</span>
+<span id="408"> 408</span>
+<span id="409"> 409</span>
+<span id="410"> 410</span>
+<span id="411"> 411</span>
+<span id="412"> 412</span>
+<span id="413"> 413</span>
+<span id="414"> 414</span>
+<span id="415"> 415</span>
+<span id="416"> 416</span>
+<span id="417"> 417</span>
+<span id="418"> 418</span>
+<span id="419"> 419</span>
+<span id="420"> 420</span>
+<span id="421"> 421</span>
+<span id="422"> 422</span>
+<span id="423"> 423</span>
+<span id="424"> 424</span>
+<span id="425"> 425</span>
+<span id="426"> 426</span>
+<span id="427"> 427</span>
+<span id="428"> 428</span>
+<span id="429"> 429</span>
+<span id="430"> 430</span>
+<span id="431"> 431</span>
+<span id="432"> 432</span>
+<span id="433"> 433</span>
+<span id="434"> 434</span>
+<span id="435"> 435</span>
+<span id="436"> 436</span>
+<span id="437"> 437</span>
+<span id="438"> 438</span>
+<span id="439"> 439</span>
+<span id="440"> 440</span>
+<span id="441"> 441</span>
+<span id="442"> 442</span>
+<span id="443"> 443</span>
+<span id="444"> 444</span>
+<span id="445"> 445</span>
+<span id="446"> 446</span>
+<span id="447"> 447</span>
+<span id="448"> 448</span>
+<span id="449"> 449</span>
+<span id="450"> 450</span>
+<span id="451"> 451</span>
+<span id="452"> 452</span>
+<span id="453"> 453</span>
+<span id="454"> 454</span>
+<span id="455"> 455</span>
+<span id="456"> 456</span>
+<span id="457"> 457</span>
+<span id="458"> 458</span>
+<span id="459"> 459</span>
+<span id="460"> 460</span>
+<span id="461"> 461</span>
+<span id="462"> 462</span>
+<span id="463"> 463</span>
+<span id="464"> 464</span>
+<span id="465"> 465</span>
+<span id="466"> 466</span>
+<span id="467"> 467</span>
+<span id="468"> 468</span>
+<span id="469"> 469</span>
+<span id="470"> 470</span>
+<span id="471"> 471</span>
+<span id="472"> 472</span>
+<span id="473"> 473</span>
+<span id="474"> 474</span>
+<span id="475"> 475</span>
+<span id="476"> 476</span>
+<span id="477"> 477</span>
+<span id="478"> 478</span>
+<span id="479"> 479</span>
+<span id="480"> 480</span>
+<span id="481"> 481</span>
+<span id="482"> 482</span>
+<span id="483"> 483</span>
+<span id="484"> 484</span>
+<span id="485"> 485</span>
+<span id="486"> 486</span>
+<span id="487"> 487</span>
+<span id="488"> 488</span>
+<span id="489"> 489</span>
+<span id="490"> 490</span>
+<span id="491"> 491</span>
+<span id="492"> 492</span>
+<span id="493"> 493</span>
+<span id="494"> 494</span>
+<span id="495"> 495</span>
+<span id="496"> 496</span>
+<span id="497"> 497</span>
+<span id="498"> 498</span>
+<span id="499"> 499</span>
+<span id="500"> 500</span>
+<span id="501"> 501</span>
+<span id="502"> 502</span>
+<span id="503"> 503</span>
+<span id="504"> 504</span>
+<span id="505"> 505</span>
+<span id="506"> 506</span>
+<span id="507"> 507</span>
+<span id="508"> 508</span>
+<span id="509"> 509</span>
+<span id="510"> 510</span>
+<span id="511"> 511</span>
+<span id="512"> 512</span>
+<span id="513"> 513</span>
+<span id="514"> 514</span>
+<span id="515"> 515</span>
+<span id="516"> 516</span>
+<span id="517"> 517</span>
+<span id="518"> 518</span>
+<span id="519"> 519</span>
+<span id="520"> 520</span>
+<span id="521"> 521</span>
+<span id="522"> 522</span>
+<span id="523"> 523</span>
+<span id="524"> 524</span>
+<span id="525"> 525</span>
+<span id="526"> 526</span>
+<span id="527"> 527</span>
+<span id="528"> 528</span>
+<span id="529"> 529</span>
+<span id="530"> 530</span>
+<span id="531"> 531</span>
+<span id="532"> 532</span>
+<span id="533"> 533</span>
+<span id="534"> 534</span>
+<span id="535"> 535</span>
+<span id="536"> 536</span>
+<span id="537"> 537</span>
+<span id="538"> 538</span>
+<span id="539"> 539</span>
+<span id="540"> 540</span>
+<span id="541"> 541</span>
+<span id="542"> 542</span>
+<span id="543"> 543</span>
+<span id="544"> 544</span>
+<span id="545"> 545</span>
+<span id="546"> 546</span>
+<span id="547"> 547</span>
+<span id="548"> 548</span>
+<span id="549"> 549</span>
+<span id="550"> 550</span>
+<span id="551"> 551</span>
+<span id="552"> 552</span>
+<span id="553"> 553</span>
+<span id="554"> 554</span>
+<span id="555"> 555</span>
+<span id="556"> 556</span>
+<span id="557"> 557</span>
+<span id="558"> 558</span>
+<span id="559"> 559</span>
+<span id="560"> 560</span>
+<span id="561"> 561</span>
+<span id="562"> 562</span>
+<span id="563"> 563</span>
+<span id="564"> 564</span>
+<span id="565"> 565</span>
+<span id="566"> 566</span>
+<span id="567"> 567</span>
+<span id="568"> 568</span>
+<span id="569"> 569</span>
+<span id="570"> 570</span>
+<span id="571"> 571</span>
+<span id="572"> 572</span>
+<span id="573"> 573</span>
+<span id="574"> 574</span>
+<span id="575"> 575</span>
+<span id="576"> 576</span>
+<span id="577"> 577</span>
+<span id="578"> 578</span>
+<span id="579"> 579</span>
+<span id="580"> 580</span>
+<span id="581"> 581</span>
+<span id="582"> 582</span>
+<span id="583"> 583</span>
+<span id="584"> 584</span>
+<span id="585"> 585</span>
+<span id="586"> 586</span>
+<span id="587"> 587</span>
+<span id="588"> 588</span>
+<span id="589"> 589</span>
+<span id="590"> 590</span>
+<span id="591"> 591</span>
+<span id="592"> 592</span>
+<span id="593"> 593</span>
+<span id="594"> 594</span>
+<span id="595"> 595</span>
+<span id="596"> 596</span>
+<span id="597"> 597</span>
+<span id="598"> 598</span>
+<span id="599"> 599</span>
+<span id="600"> 600</span>
+<span id="601"> 601</span>
+<span id="602"> 602</span>
+<span id="603"> 603</span>
+<span id="604"> 604</span>
+<span id="605"> 605</span>
+<span id="606"> 606</span>
+<span id="607"> 607</span>
+<span id="608"> 608</span>
+<span id="609"> 609</span>
+<span id="610"> 610</span>
+<span id="611"> 611</span>
+<span id="612"> 612</span>
+<span id="613"> 613</span>
+<span id="614"> 614</span>
+<span id="615"> 615</span>
+<span id="616"> 616</span>
+<span id="617"> 617</span>
+<span id="618"> 618</span>
+<span id="619"> 619</span>
+<span id="620"> 620</span>
+<span id="621"> 621</span>
+<span id="622"> 622</span>
+<span id="623"> 623</span>
+<span id="624"> 624</span>
+<span id="625"> 625</span>
+<span id="626"> 626</span>
+<span id="627"> 627</span>
+<span id="628"> 628</span>
+<span id="629"> 629</span>
+<span id="630"> 630</span>
+<span id="631"> 631</span>
+<span id="632"> 632</span>
+<span id="633"> 633</span>
+<span id="634"> 634</span>
+<span id="635"> 635</span>
+<span id="636"> 636</span>
+<span id="637"> 637</span>
+<span id="638"> 638</span>
+<span id="639"> 639</span>
+<span id="640"> 640</span>
+<span id="641"> 641</span>
+<span id="642"> 642</span>
+<span id="643"> 643</span>
+<span id="644"> 644</span>
+<span id="645"> 645</span>
+<span id="646"> 646</span>
+<span id="647"> 647</span>
+<span id="648"> 648</span>
+<span id="649"> 649</span>
+<span id="650"> 650</span>
+<span id="651"> 651</span>
+<span id="652"> 652</span>
+<span id="653"> 653</span>
+<span id="654"> 654</span>
+<span id="655"> 655</span>
+<span id="656"> 656</span>
+<span id="657"> 657</span>
+<span id="658"> 658</span>
+<span id="659"> 659</span>
+<span id="660"> 660</span>
+<span id="661"> 661</span>
+<span id="662"> 662</span>
+<span id="663"> 663</span>
+<span id="664"> 664</span>
+<span id="665"> 665</span>
+<span id="666"> 666</span>
+<span id="667"> 667</span>
+<span id="668"> 668</span>
+<span id="669"> 669</span>
+<span id="670"> 670</span>
+<span id="671"> 671</span>
+<span id="672"> 672</span>
+<span id="673"> 673</span>
+<span id="674"> 674</span>
+<span id="675"> 675</span>
+<span id="676"> 676</span>
+<span id="677"> 677</span>
+<span id="678"> 678</span>
+<span id="679"> 679</span>
+<span id="680"> 680</span>
+<span id="681"> 681</span>
+<span id="682"> 682</span>
+<span id="683"> 683</span>
+<span id="684"> 684</span>
+<span id="685"> 685</span>
+<span id="686"> 686</span>
+<span id="687"> 687</span>
+<span id="688"> 688</span>
+<span id="689"> 689</span>
+<span id="690"> 690</span>
+<span id="691"> 691</span>
+<span id="692"> 692</span>
+<span id="693"> 693</span>
+<span id="694"> 694</span>
+<span id="695"> 695</span>
+<span id="696"> 696</span>
+<span id="697"> 697</span>
+<span id="698"> 698</span>
+<span id="699"> 699</span>
+<span id="700"> 700</span>
+<span id="701"> 701</span>
+<span id="702"> 702</span>
+<span id="703"> 703</span>
+<span id="704"> 704</span>
+<span id="705"> 705</span>
+<span id="706"> 706</span>
+<span id="707"> 707</span>
+<span id="708"> 708</span>
+<span id="709"> 709</span>
+<span id="710"> 710</span>
+<span id="711"> 711</span>
+<span id="712"> 712</span>
+<span id="713"> 713</span>
+<span id="714"> 714</span>
+<span id="715"> 715</span>
+<span id="716"> 716</span>
+<span id="717"> 717</span>
+<span id="718"> 718</span>
+<span id="719"> 719</span>
+<span id="720"> 720</span>
+<span id="721"> 721</span>
+<span id="722"> 722</span>
+<span id="723"> 723</span>
+<span id="724"> 724</span>
+<span id="725"> 725</span>
+<span id="726"> 726</span>
+<span id="727"> 727</span>
+<span id="728"> 728</span>
+<span id="729"> 729</span>
+<span id="730"> 730</span>
+<span id="731"> 731</span>
+<span id="732"> 732</span>
+<span id="733"> 733</span>
+<span id="734"> 734</span>
+<span id="735"> 735</span>
+<span id="736"> 736</span>
+<span id="737"> 737</span>
+<span id="738"> 738</span>
+<span id="739"> 739</span>
+<span id="740"> 740</span>
+<span id="741"> 741</span>
+<span id="742"> 742</span>
+<span id="743"> 743</span>
+<span id="744"> 744</span>
+<span id="745"> 745</span>
+<span id="746"> 746</span>
+<span id="747"> 747</span>
+<span id="748"> 748</span>
+<span id="749"> 749</span>
+<span id="750"> 750</span>
+<span id="751"> 751</span>
+<span id="752"> 752</span>
+<span id="753"> 753</span>
+<span id="754"> 754</span>
+<span id="755"> 755</span>
+<span id="756"> 756</span>
+<span id="757"> 757</span>
+<span id="758"> 758</span>
+<span id="759"> 759</span>
+<span id="760"> 760</span>
+<span id="761"> 761</span>
+<span id="762"> 762</span>
+<span id="763"> 763</span>
+<span id="764"> 764</span>
+<span id="765"> 765</span>
+<span id="766"> 766</span>
+<span id="767"> 767</span>
+<span id="768"> 768</span>
+<span id="769"> 769</span>
+<span id="770"> 770</span>
+<span id="771"> 771</span>
+<span id="772"> 772</span>
+<span id="773"> 773</span>
+<span id="774"> 774</span>
+<span id="775"> 775</span>
+<span id="776"> 776</span>
+<span id="777"> 777</span>
+<span id="778"> 778</span>
+<span id="779"> 779</span>
+<span id="780"> 780</span>
+<span id="781"> 781</span>
+<span id="782"> 782</span>
+<span id="783"> 783</span>
+<span id="784"> 784</span>
+<span id="785"> 785</span>
+<span id="786"> 786</span>
+<span id="787"> 787</span>
+<span id="788"> 788</span>
+<span id="789"> 789</span>
+<span id="790"> 790</span>
+<span id="791"> 791</span>
+<span id="792"> 792</span>
+<span id="793"> 793</span>
+<span id="794"> 794</span>
+<span id="795"> 795</span>
+<span id="796"> 796</span>
+<span id="797"> 797</span>
+<span id="798"> 798</span>
+<span id="799"> 799</span>
+<span id="800"> 800</span>
+<span id="801"> 801</span>
+<span id="802"> 802</span>
+<span id="803"> 803</span>
+<span id="804"> 804</span>
+<span id="805"> 805</span>
+<span id="806"> 806</span>
+<span id="807"> 807</span>
+<span id="808"> 808</span>
+<span id="809"> 809</span>
+<span id="810"> 810</span>
+<span id="811"> 811</span>
+<span id="812"> 812</span>
+<span id="813"> 813</span>
+<span id="814"> 814</span>
+<span id="815"> 815</span>
+<span id="816"> 816</span>
+<span id="817"> 817</span>
+<span id="818"> 818</span>
+<span id="819"> 819</span>
+<span id="820"> 820</span>
+<span id="821"> 821</span>
+<span id="822"> 822</span>
+<span id="823"> 823</span>
+<span id="824"> 824</span>
+<span id="825"> 825</span>
+<span id="826"> 826</span>
+<span id="827"> 827</span>
+<span id="828"> 828</span>
+<span id="829"> 829</span>
+<span id="830"> 830</span>
+<span id="831"> 831</span>
+<span id="832"> 832</span>
+<span id="833"> 833</span>
+<span id="834"> 834</span>
+<span id="835"> 835</span>
+<span id="836"> 836</span>
+<span id="837"> 837</span>
+<span id="838"> 838</span>
+<span id="839"> 839</span>
+<span id="840"> 840</span>
+<span id="841"> 841</span>
+<span id="842"> 842</span>
+<span id="843"> 843</span>
+<span id="844"> 844</span>
+<span id="845"> 845</span>
+<span id="846"> 846</span>
+<span id="847"> 847</span>
+<span id="848"> 848</span>
+<span id="849"> 849</span>
+<span id="850"> 850</span>
+<span id="851"> 851</span>
+<span id="852"> 852</span>
+<span id="853"> 853</span>
+<span id="854"> 854</span>
+<span id="855"> 855</span>
+<span id="856"> 856</span>
+<span id="857"> 857</span>
+<span id="858"> 858</span>
+<span id="859"> 859</span>
+<span id="860"> 860</span>
+<span id="861"> 861</span>
+<span id="862"> 862</span>
+<span id="863"> 863</span>
+<span id="864"> 864</span>
+<span id="865"> 865</span>
+<span id="866"> 866</span>
+<span id="867"> 867</span>
+<span id="868"> 868</span>
+<span id="869"> 869</span>
+<span id="870"> 870</span>
+<span id="871"> 871</span>
+<span id="872"> 872</span>
+<span id="873"> 873</span>
+<span id="874"> 874</span>
+<span id="875"> 875</span>
+<span id="876"> 876</span>
+<span id="877"> 877</span>
+<span id="878"> 878</span>
+<span id="879"> 879</span>
+<span id="880"> 880</span>
+<span id="881"> 881</span>
+<span id="882"> 882</span>
+<span id="883"> 883</span>
+<span id="884"> 884</span>
+<span id="885"> 885</span>
+<span id="886"> 886</span>
+<span id="887"> 887</span>
+<span id="888"> 888</span>
+<span id="889"> 889</span>
+<span id="890"> 890</span>
+<span id="891"> 891</span>
+<span id="892"> 892</span>
+<span id="893"> 893</span>
+<span id="894"> 894</span>
+<span id="895"> 895</span>
+<span id="896"> 896</span>
+<span id="897"> 897</span>
+<span id="898"> 898</span>
+<span id="899"> 899</span>
+<span id="900"> 900</span>
+<span id="901"> 901</span>
+<span id="902"> 902</span>
+<span id="903"> 903</span>
+<span id="904"> 904</span>
+<span id="905"> 905</span>
+<span id="906"> 906</span>
+<span id="907"> 907</span>
+<span id="908"> 908</span>
+<span id="909"> 909</span>
+<span id="910"> 910</span>
+<span id="911"> 911</span>
+<span id="912"> 912</span>
+<span id="913"> 913</span>
+<span id="914"> 914</span>
+<span id="915"> 915</span>
+<span id="916"> 916</span>
+<span id="917"> 917</span>
+<span id="918"> 918</span>
+<span id="919"> 919</span>
+<span id="920"> 920</span>
+<span id="921"> 921</span>
+<span id="922"> 922</span>
+<span id="923"> 923</span>
+<span id="924"> 924</span>
+<span id="925"> 925</span>
+<span id="926"> 926</span>
+<span id="927"> 927</span>
+<span id="928"> 928</span>
+<span id="929"> 929</span>
+<span id="930"> 930</span>
+<span id="931"> 931</span>
+<span id="932"> 932</span>
+<span id="933"> 933</span>
+<span id="934"> 934</span>
+<span id="935"> 935</span>
+<span id="936"> 936</span>
+<span id="937"> 937</span>
+<span id="938"> 938</span>
+<span id="939"> 939</span>
+<span id="940"> 940</span>
+<span id="941"> 941</span>
+<span id="942"> 942</span>
+<span id="943"> 943</span>
+<span id="944"> 944</span>
+<span id="945"> 945</span>
+<span id="946"> 946</span>
+<span id="947"> 947</span>
+<span id="948"> 948</span>
+<span id="949"> 949</span>
+<span id="950"> 950</span>
+<span id="951"> 951</span>
+<span id="952"> 952</span>
+<span id="953"> 953</span>
+<span id="954"> 954</span>
+<span id="955"> 955</span>
+<span id="956"> 956</span>
+<span id="957"> 957</span>
+<span id="958"> 958</span>
+<span id="959"> 959</span>
+<span id="960"> 960</span>
+<span id="961"> 961</span>
+<span id="962"> 962</span>
+<span id="963"> 963</span>
+<span id="964"> 964</span>
+<span id="965"> 965</span>
+<span id="966"> 966</span>
+<span id="967"> 967</span>
+<span id="968"> 968</span>
+<span id="969"> 969</span>
+<span id="970"> 970</span>
+<span id="971"> 971</span>
+<span id="972"> 972</span>
+<span id="973"> 973</span>
+<span id="974"> 974</span>
+<span id="975"> 975</span>
+<span id="976"> 976</span>
+<span id="977"> 977</span>
+<span id="978"> 978</span>
+<span id="979"> 979</span>
+<span id="980"> 980</span>
+<span id="981"> 981</span>
+<span id="982"> 982</span>
+<span id="983"> 983</span>
+<span id="984"> 984</span>
+<span id="985"> 985</span>
+<span id="986"> 986</span>
+<span id="987"> 987</span>
+<span id="988"> 988</span>
+<span id="989"> 989</span>
+<span id="990"> 990</span>
+<span id="991"> 991</span>
+<span id="992"> 992</span>
+<span id="993"> 993</span>
+<span id="994"> 994</span>
+<span id="995"> 995</span>
+<span id="996"> 996</span>
+<span id="997"> 997</span>
+<span id="998"> 998</span>
+<span id="999"> 999</span>
+<span id="1000">1000</span>
+<span id="1001">1001</span>
+<span id="1002">1002</span>
+<span id="1003">1003</span>
+<span id="1004">1004</span>
+<span id="1005">1005</span>
+<span id="1006">1006</span>
+<span id="1007">1007</span>
+<span id="1008">1008</span>
+<span id="1009">1009</span>
+<span id="1010">1010</span>
+<span id="1011">1011</span>
+<span id="1012">1012</span>
+<span id="1013">1013</span>
+<span id="1014">1014</span>
+<span id="1015">1015</span>
+<span id="1016">1016</span>
+<span id="1017">1017</span>
+<span id="1018">1018</span>
+<span id="1019">1019</span>
+<span id="1020">1020</span>
+<span id="1021">1021</span>
+<span id="1022">1022</span>
+<span id="1023">1023</span>
+<span id="1024">1024</span>
+<span id="1025">1025</span>
+<span id="1026">1026</span>
+<span id="1027">1027</span>
+<span id="1028">1028</span>
+<span id="1029">1029</span>
+<span id="1030">1030</span>
+<span id="1031">1031</span>
+<span id="1032">1032</span>
+<span id="1033">1033</span>
+<span id="1034">1034</span>
+<span id="1035">1035</span>
+<span id="1036">1036</span>
+<span id="1037">1037</span>
+<span id="1038">1038</span>
+<span id="1039">1039</span>
+<span id="1040">1040</span>
+<span id="1041">1041</span>
+<span id="1042">1042</span>
+<span id="1043">1043</span>
+<span id="1044">1044</span>
+<span id="1045">1045</span>
+<span id="1046">1046</span>
+<span id="1047">1047</span>
+<span id="1048">1048</span>
+<span id="1049">1049</span>
+<span id="1050">1050</span>
+<span id="1051">1051</span>
+<span id="1052">1052</span>
+<span id="1053">1053</span>
+<span id="1054">1054</span>
+<span id="1055">1055</span>
+<span id="1056">1056</span>
+<span id="1057">1057</span>
+<span id="1058">1058</span>
+<span id="1059">1059</span>
+<span id="1060">1060</span>
+<span id="1061">1061</span>
+<span id="1062">1062</span>
+<span id="1063">1063</span>
+<span id="1064">1064</span>
+<span id="1065">1065</span>
+<span id="1066">1066</span>
+<span id="1067">1067</span>
+<span id="1068">1068</span>
+<span id="1069">1069</span>
+<span id="1070">1070</span>
+<span id="1071">1071</span>
+<span id="1072">1072</span>
+<span id="1073">1073</span>
+<span id="1074">1074</span>
+<span id="1075">1075</span>
+<span id="1076">1076</span>
+<span id="1077">1077</span>
+<span id="1078">1078</span>
+<span id="1079">1079</span>
+<span id="1080">1080</span>
+<span id="1081">1081</span>
+<span id="1082">1082</span>
+<span id="1083">1083</span>
+<span id="1084">1084</span>
+<span id="1085">1085</span>
+<span id="1086">1086</span>
+<span id="1087">1087</span>
+<span id="1088">1088</span>
+<span id="1089">1089</span>
+<span id="1090">1090</span>
+<span id="1091">1091</span>
+<span id="1092">1092</span>
+<span id="1093">1093</span>
+<span id="1094">1094</span>
+<span id="1095">1095</span>
+<span id="1096">1096</span>
+<span id="1097">1097</span>
+<span id="1098">1098</span>
+<span id="1099">1099</span>
+<span id="1100">1100</span>
+<span id="1101">1101</span>
+<span id="1102">1102</span>
+<span id="1103">1103</span>
+<span id="1104">1104</span>
+<span id="1105">1105</span>
+<span id="1106">1106</span>
+<span id="1107">1107</span>
+<span id="1108">1108</span>
+<span id="1109">1109</span>
+<span id="1110">1110</span>
+<span id="1111">1111</span>
+<span id="1112">1112</span>
+<span id="1113">1113</span>
+<span id="1114">1114</span>
+<span id="1115">1115</span>
+<span id="1116">1116</span>
+<span id="1117">1117</span>
+<span id="1118">1118</span>
+<span id="1119">1119</span>
+<span id="1120">1120</span>
+<span id="1121">1121</span>
+<span id="1122">1122</span>
+<span id="1123">1123</span>
+<span id="1124">1124</span>
+<span id="1125">1125</span>
+<span id="1126">1126</span>
+<span id="1127">1127</span>
+<span id="1128">1128</span>
+<span id="1129">1129</span>
+<span id="1130">1130</span>
+<span id="1131">1131</span>
+<span id="1132">1132</span>
+<span id="1133">1133</span>
+<span id="1134">1134</span>
+<span id="1135">1135</span>
+<span id="1136">1136</span>
+<span id="1137">1137</span>
+<span id="1138">1138</span>
+<span id="1139">1139</span>
+<span id="1140">1140</span>
+<span id="1141">1141</span>
+<span id="1142">1142</span>
+<span id="1143">1143</span>
+<span id="1144">1144</span>
+<span id="1145">1145</span>
+<span id="1146">1146</span>
+<span id="1147">1147</span>
+<span id="1148">1148</span>
+<span id="1149">1149</span>
+<span id="1150">1150</span>
+<span id="1151">1151</span>
+<span id="1152">1152</span>
+<span id="1153">1153</span>
+<span id="1154">1154</span>
+<span id="1155">1155</span>
+<span id="1156">1156</span>
+<span id="1157">1157</span>
+<span id="1158">1158</span>
+<span id="1159">1159</span>
+<span id="1160">1160</span>
+<span id="1161">1161</span>
+<span id="1162">1162</span>
+<span id="1163">1163</span>
+<span id="1164">1164</span>
+<span id="1165">1165</span>
+<span id="1166">1166</span>
+<span id="1167">1167</span>
+<span id="1168">1168</span>
+<span id="1169">1169</span>
+<span id="1170">1170</span>
+<span id="1171">1171</span>
+<span id="1172">1172</span>
+<span id="1173">1173</span>
+<span id="1174">1174</span>
+<span id="1175">1175</span>
+<span id="1176">1176</span>
+<span id="1177">1177</span>
+<span id="1178">1178</span>
+<span id="1179">1179</span>
+<span id="1180">1180</span>
+<span id="1181">1181</span>
+<span id="1182">1182</span>
+<span id="1183">1183</span>
+<span id="1184">1184</span>
+<span id="1185">1185</span>
+<span id="1186">1186</span>
+<span id="1187">1187</span>
+<span id="1188">1188</span>
+<span id="1189">1189</span>
+<span id="1190">1190</span>
+</pre><pre class="rust">
+<span class="doccomment">//! This crate will help you to write simpler tests by leveraging a software testing concept called</span>
+<span class="doccomment">//! [test fixtures](https://en.wikipedia.org/wiki/Test_fixture#Software). A fixture is something</span>
+<span class="doccomment">//! that you can use in your tests to encapsulate a test&#39;s dependencies.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! The general idea is to have smaller tests that only describe the thing you&#39;re testing while you</span>
+<span class="doccomment">//! hide the auxiliary utilities your tests make use of somewhere else.</span>
+<span class="doccomment">//! For instance, if you have an application that has many tests with users, shopping baskets, and</span>
+<span class="doccomment">//! products, you&#39;d have to create a user, a shopping basket, and product every single time in</span>
+<span class="doccomment">//! every test which becomes unwieldy quickly. In order to cut down on that repetition, you can</span>
+<span class="doccomment">//! instead use fixtures to declare that you need those objects for your function and the fixtures</span>
+<span class="doccomment">//! will take care of creating those by themselves. Focus on the important stuff in your tests!</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! In `rstest` a fixture is a function that can return any kind of valid Rust type. This</span>
+<span class="doccomment">//! effectively means that your fixtures are not limited by the kind of data they can return.</span>
+<span class="doccomment">//! A test can consume an arbitrary number of fixtures at the same time.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## What</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! The `rstest` crate defines the following procedural macros:</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! - [`[rstest]`](macro@rstest): Declare that a test or a group of tests that may take</span>
+<span class="doccomment">//! [fixtures](attr.rstest.html#injecting-fixtures),</span>
+<span class="doccomment">//! [input table](attr.rstest.html#test-parametrized-cases) or</span>
+<span class="doccomment">//! [list of values](attr.rstest.html#values-lists).</span>
+<span class="doccomment">//! - [`[fixture]`](macro@fixture): To mark a function as a fixture.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## Why</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! Very often in Rust we write tests like this</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! #[test]</span>
+<span class="doccomment">//! fn should_process_two_users() {</span>
+<span class="doccomment">//!     let mut repository = create_repository();</span>
+<span class="doccomment">//!     repository.add(&quot;Bob&quot;, 21);</span>
+<span class="doccomment">//!     repository.add(&quot;Alice&quot;, 22);</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     let processor = string_processor();</span>
+<span class="doccomment">//!     processor.send_all(&amp;repository, &quot;Good Morning&quot;);</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     assert_eq!(2, processor.output.find(&quot;Good Morning&quot;).count());</span>
+<span class="doccomment">//!     assert!(processor.output.contains(&quot;Bob&quot;));</span>
+<span class="doccomment">//!     assert!(processor.output.contains(&quot;Alice&quot;));</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! By making use of [`[rstest]`](macro@rstest) we can isolate the dependencies `empty_repository` and</span>
+<span class="doccomment">//! `string_processor` by passing them as fixtures:</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! # use rstest::*;</span>
+<span class="doccomment">//! #[rstest]</span>
+<span class="doccomment">//! fn should_process_two_users(mut empty_repository: impl Repository,</span>
+<span class="doccomment">//!                             string_processor: FakeProcessor) {</span>
+<span class="doccomment">//!     empty_repository.add(&quot;Bob&quot;, 21);</span>
+<span class="doccomment">//!     empty_repository.add(&quot;Alice&quot;, 22);</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     string_processor.send_all(&quot;Good Morning&quot;);</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     assert_eq!(2, string_processor.output.find(&quot;Good Morning&quot;).count());</span>
+<span class="doccomment">//!     assert!(string_processor.output.contains(&quot;Bob&quot;));</span>
+<span class="doccomment">//!     assert!(string_processor.output.contains(&quot;Alice&quot;));</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ... or if you use `&quot;Alice&quot;` and `&quot;Bob&quot;` in other tests, you can isolate `alice_and_bob` fixture</span>
+<span class="doccomment">//! and use it directly:</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! # use rstest::*;</span>
+<span class="doccomment">//! # trait Repository { fn add(&amp;mut self, name: &amp;str, age: u8); }</span>
+<span class="doccomment">//! # struct Rep;</span>
+<span class="doccomment">//! # impl Repository for Rep { fn add(&amp;mut self, name: &amp;str, age: u8) {} }</span>
+<span class="doccomment">//! # #[fixture]</span>
+<span class="doccomment">//! # fn empty_repository() -&gt; Rep {</span>
+<span class="doccomment">//! #     Rep</span>
+<span class="doccomment">//! # }</span>
+<span class="doccomment">//! #[fixture]</span>
+<span class="doccomment">//! fn alice_and_bob(mut empty_repository: impl Repository) -&gt; impl Repository {</span>
+<span class="doccomment">//!     empty_repository.add(&quot;Bob&quot;, 21);</span>
+<span class="doccomment">//!     empty_repository.add(&quot;Alice&quot;, 22);</span>
+<span class="doccomment">//!     empty_repository</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! #[rstest]</span>
+<span class="doccomment">//! fn should_process_two_users(alice_and_bob: impl Repository,</span>
+<span class="doccomment">//!                             string_processor: FakeProcessor) {</span>
+<span class="doccomment">//!     string_processor.send_all(&quot;Good Morning&quot;);</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     assert_eq!(2, string_processor.output.find(&quot;Good Morning&quot;).count());</span>
+<span class="doccomment">//!     assert!(string_processor.output.contains(&quot;Bob&quot;));</span>
+<span class="doccomment">//!     assert!(string_processor.output.contains(&quot;Alice&quot;));</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## Injecting fixtures as function arguments</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! `rstest` functions can receive fixtures by using them as input arguments.</span>
+<span class="doccomment">//! A function decorated with [`[rstest]`](attr.rstest.html#injecting-fixtures)</span>
+<span class="doccomment">//! will resolve each argument name by call the fixture function.</span>
+<span class="doccomment">//! Fixtures should be annotated with the [`[fixture]`](macro@fixture) attribute.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! Fixtures will be resolved like function calls by following the standard resolution rules.</span>
+<span class="doccomment">//! Therefore, an identically named fixture can be use in different context.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! # use rstest::*;</span>
+<span class="doccomment">//! # trait Repository { }</span>
+<span class="doccomment">//! # #[derive(Default)]</span>
+<span class="doccomment">//! # struct DataSet {}</span>
+<span class="doccomment">//! # impl Repository for DataSet { }</span>
+<span class="doccomment">//! mod empty_cases {</span>
+<span class="doccomment">//! # use rstest::*;</span>
+<span class="doccomment">//! # trait Repository { }</span>
+<span class="doccomment">//! # #[derive(Default)]</span>
+<span class="doccomment">//! # struct DataSet {}</span>
+<span class="doccomment">//! # impl Repository for DataSet { }</span>
+<span class="doccomment">//!     use super::*;</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     #[fixture]</span>
+<span class="doccomment">//!     fn repository() -&gt; impl Repository {</span>
+<span class="doccomment">//!         DataSet::default()</span>
+<span class="doccomment">//!     }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     #[rstest]</span>
+<span class="doccomment">//!     fn should_do_nothing(repository: impl Repository) {</span>
+<span class="doccomment">//!         //.. test impl ..</span>
+<span class="doccomment">//!     }</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! mod non_trivial_case {</span>
+<span class="doccomment">//! # use rstest::*;</span>
+<span class="doccomment">//! # trait Repository { }</span>
+<span class="doccomment">//! # #[derive(Default)]</span>
+<span class="doccomment">//! # struct DataSet {}</span>
+<span class="doccomment">//! # impl Repository for DataSet { }</span>
+<span class="doccomment">//!     use super::*;</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     #[fixture]</span>
+<span class="doccomment">//!     fn repository() -&gt; impl Repository {</span>
+<span class="doccomment">//!         let mut ds = DataSet::default();</span>
+<span class="doccomment">//!         // Fill your dataset with interesting case</span>
+<span class="doccomment">//!         ds</span>
+<span class="doccomment">//!     }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//!     #[rstest]</span>
+<span class="doccomment">//!     fn should_notify_all_entries(repository: impl Repository) {</span>
+<span class="doccomment">//!         //.. test impl ..</span>
+<span class="doccomment">//!     }</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! Last but not least, fixtures can be injected like we saw in `alice_and_bob` example.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## Creating parametrized tests</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! You can use also [`[rstest]`](attr.rstest.html#test-parametrized-cases) to create</span>
+<span class="doccomment">//! simple table-based tests. Let&#39;s see the classic Fibonacci example:</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! use rstest::rstest;</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! #[rstest]</span>
+<span class="doccomment">//! #[case(0, 0)]</span>
+<span class="doccomment">//! #[case(1, 1)]</span>
+<span class="doccomment">//! #[case(2, 1)]</span>
+<span class="doccomment">//! #[case(3, 2)]</span>
+<span class="doccomment">//! #[case(4, 3)]</span>
+<span class="doccomment">//! #[case(5, 5)]</span>
+<span class="doccomment">//! #[case(6, 8)]</span>
+<span class="doccomment">//! fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {</span>
+<span class="doccomment">//!     assert_eq!(expected, fibonacci(input))</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! fn fibonacci(input: u32) -&gt; u32 {</span>
+<span class="doccomment">//!     match input {</span>
+<span class="doccomment">//!         0 =&gt; 0,</span>
+<span class="doccomment">//!         1 =&gt; 1,</span>
+<span class="doccomment">//!         n =&gt; fibonacci(n - 2) + fibonacci(n - 1)</span>
+<span class="doccomment">//!     }</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! This will generate a bunch of tests, one for every `#[case(a, b)]`.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## Creating a test for each combinations of given values</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! In some cases you need to test your code for each combinations of some input values. In this</span>
+<span class="doccomment">//! cases [`[rstest]`](attr.rstest.html#values-lists) give you the ability to define a list</span>
+<span class="doccomment">//! of values (rust expressions) to use for an arguments.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! # use rstest::rstest;</span>
+<span class="doccomment">//! # #[derive(PartialEq, Debug)]</span>
+<span class="doccomment">//! # enum State { Init, Start, Processing, Terminated }</span>
+<span class="doccomment">//! # #[derive(PartialEq, Debug)]</span>
+<span class="doccomment">//! # enum Event { Error, Fatal }</span>
+<span class="doccomment">//! # impl State { fn process(self, event: Event) -&gt; Self { self } }</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! #[rstest]</span>
+<span class="doccomment">//! fn should_terminate(</span>
+<span class="doccomment">//!     #[values(State::Init, State::Start, State::Processing)]</span>
+<span class="doccomment">//!     state: State,</span>
+<span class="doccomment">//!     #[values(Event::Error, Event::Fatal)]</span>
+<span class="doccomment">//!     event: Event</span>
+<span class="doccomment">//! ) {</span>
+<span class="doccomment">//!     assert_eq!(State::Terminated, state.process(event))</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! This will generate a test for each combination of `state` and `event`.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ## Magic Conversion</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! If you need a value where its type implement `FromStr()` trait you</span>
+<span class="doccomment">//! can use a literal string to build it.</span>
+<span class="doccomment">//!</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! # use rstest::rstest;</span>
+<span class="doccomment">//! # use std::net::SocketAddr;</span>
+<span class="doccomment">//! #[rstest]</span>
+<span class="doccomment">//! #[case(&quot;1.2.3.4:8080&quot;, 8080)]</span>
+<span class="doccomment">//! #[case(&quot;127.0.0.1:9000&quot;, 9000)]</span>
+<span class="doccomment">//! fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) {</span>
+<span class="doccomment">//!     assert_eq!(expected, addr.port());</span>
+<span class="doccomment">//! }</span>
+<span class="doccomment">//! ```</span>
+<span class="doccomment">//! You can use this feature also in value list and in fixture default value.</span>
+
+<span class="attribute">#![<span class="ident">cfg_attr</span>(<span class="ident">use_proc_macro_diagnostic</span>, <span class="ident">feature</span>(<span class="ident">proc_macro_diagnostic</span>))]</span>
+<span class="kw">extern</span> <span class="kw">crate</span> <span class="ident">proc_macro</span>;
+
+<span class="comment">// Test utility module</span>
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">test</span>;
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">use</span> <span class="ident">rstest_reuse</span>;
+
+<span class="attribute">#[<span class="ident">macro_use</span>]</span>
+<span class="kw">mod</span> <span class="ident">error</span>;
+<span class="kw">mod</span> <span class="ident">parse</span>;
+<span class="kw">mod</span> <span class="ident">refident</span>;
+<span class="kw">mod</span> <span class="ident">render</span>;
+<span class="kw">mod</span> <span class="ident">resolver</span>;
+<span class="kw">mod</span> <span class="ident">utils</span>;
+
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">parse_macro_input</span>, <span class="ident">ItemFn</span>};
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse</span>::{<span class="ident">fixture::FixtureInfo</span>, <span class="ident">future::ReplaceFutureAttribute</span>, <span class="ident">rstest::RsTestInfo</span>};
+<span class="kw">use</span> <span class="ident">parse::ExtendWithFunctionAttrs</span>;
+<span class="kw">use</span> <span class="ident">quote::ToTokens</span>;
+
+<span class="doccomment">/// Define a fixture that you can use in all `rstest`&#39;s test arguments. You should just mark your</span>
+<span class="doccomment">/// function as `#[fixture]` and then use it as a test&#39;s argument. Fixture functions can also</span>
+<span class="doccomment">/// use other fixtures.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Let&#39;s see a trivial example:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn twenty_one() -&gt; i32 { 21 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn two() -&gt; i32 { 2 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn injected(twenty_one: i32, two: i32) -&gt; i32 { twenty_one * two }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(injected: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If the fixture function is an [`async` function](#async) your fixture become an `async`</span>
+<span class="doccomment">/// fixture.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # Default values</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you need to define argument default value you can use `#[default(expression)]`</span>
+<span class="doccomment">/// argument&#39;s attribute:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn injected(</span>
+<span class="doccomment">///     #[default(21)]</span>
+<span class="doccomment">///     twenty_one: i32,</span>
+<span class="doccomment">///     #[default(1 + 1)]</span>
+<span class="doccomment">///     two: i32</span>
+<span class="doccomment">/// ) -&gt; i32 { twenty_one * two }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(injected: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// The `expression` could be any valid rust expression, even an `async` block if you need.</span>
+<span class="doccomment">/// Moreover, if the type implements `FromStr` trait you can use a literal string to build it.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # use std::net::SocketAddr;</span>
+<span class="doccomment">/// # struct DbConnection {}</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn db_connection(</span>
+<span class="doccomment">///     #[default(&quot;127.0.0.1:9000&quot;)]</span>
+<span class="doccomment">///     addr: SocketAddr</span>
+<span class="doccomment">/// ) -&gt; DbConnection {</span>
+<span class="doccomment">///     // create connection</span>
+<span class="doccomment">/// # DbConnection{}</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # Async</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you need you can write `async` fixtures to use in your `async` tests. Simply use `async`</span>
+<span class="doccomment">/// keyword for your function and the fixture become an `async` fixture.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// async fn async_fixture() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// async fn the_test(#[future] async_fixture: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, async_fixture.await)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// The `#[future]` argument attribute helps to remove the `impl Future&lt;Output = T&gt;` boilerplate.</span>
+<span class="doccomment">/// In this case the macro expands it in:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # use std::future::Future;</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # async fn async_fixture() -&gt; i32 { 42 }</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// async fn the_test(async_fixture: impl std::future::Future&lt;Output = i32&gt;) {</span>
+<span class="doccomment">///     assert_eq!(42, async_fixture.await)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// If you need, you can use `#[future]` attribute also with an implicit lifetime reference</span>
+<span class="doccomment">/// because the macro will replace the implicit lifetime with an explicit one.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # Rename</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Sometimes you want to have long and descriptive name for your fixture but you prefer to use a much</span>
+<span class="doccomment">/// shorter name for argument that represent it in your fixture or test. You can rename the fixture</span>
+<span class="doccomment">/// using `#[from(short_name)]` attribute like following example:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn long_and_boring_descriptive_name() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(#[from(long_and_boring_descriptive_name)] short: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, short)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # Partial Injection</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// You can also partialy inject fixture dependency using `#[with(v1, v2, ..)]` attribute:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn base() -&gt; i32 { 1 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn first(base: i32) -&gt; i32 { 1 * base }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn second(base: i32) -&gt; i32 { 2 * base }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn injected(first: i32, #[with(3)] second: i32) -&gt; i32 { first * second }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(injected: i32) {</span>
+<span class="doccomment">///     assert_eq!(-6, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// Note that injected value can be an arbitrary rust expression. `#[with(v1, ..., vn)]`</span>
+<span class="doccomment">/// attribute will inject `v1, ..., vn` expression as fixture arguments: all remaining arguments</span>
+<span class="doccomment">/// will be resolved as fixtures.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Sometimes the return type cannot be infered so you must define it: For the few times you may</span>
+<span class="doccomment">/// need to do it, you can use the `#[default(type)]`, `#[partial_n(type)]` function attribute</span>
+<span class="doccomment">/// to define it:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">/// # use std::fmt::Debug;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// pub fn i() -&gt; u32 {</span>
+<span class="doccomment">///     42</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// pub fn j() -&gt; i32 {</span>
+<span class="doccomment">///     -42</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// #[default(impl Iterator&lt;Item=(u32, i32)&gt;)]</span>
+<span class="doccomment">/// #[partial_1(impl Iterator&lt;Item=(I,i32)&gt;)]</span>
+<span class="doccomment">/// pub fn fx&lt;I, J&gt;(i: I, j: J) -&gt; impl Iterator&lt;Item=(I, J)&gt; {</span>
+<span class="doccomment">///     std::iter::once((i, j))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn resolve_by_default&lt;I: Debug + PartialEq&gt;(mut fx: impl Iterator&lt;Item=I&gt;) {</span>
+<span class="doccomment">///     assert_eq!((42, -42), fx.next().unwrap())</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn resolve_partial&lt;I: Debug + PartialEq&gt;(#[with(42.0)] mut fx: impl Iterator&lt;Item=I&gt;) {</span>
+<span class="doccomment">///     assert_eq!((42.0, -42), fx.next().unwrap())</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// `partial_i` is the fixture used when you inject the first `i` arguments in test call.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # Old _compact_ syntax</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// There is also a compact form for all previous features. This will mantained for a long time</span>
+<span class="doccomment">/// but for `fixture` I strongly recomand to migrate your code because you&#39;ll pay a little</span>
+<span class="doccomment">/// verbosity but get back a more readable code.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Follow the previous examples in old _compact_ syntax.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Default</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[fixture(twenty_one=21, two=2)]</span>
+<span class="doccomment">/// fn injected(twenty_one: i32, two: i32) -&gt; i32 { twenty_one * two }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Rename</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn long_and_boring_descriptive_name() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest(long_and_boring_descriptive_name as short)]</span>
+<span class="doccomment">/// fn the_test(short: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, short)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Partial Injection</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # fn base() -&gt; i32 { 1 }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # fn first(base: i32) -&gt; i32 { 1 * base }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # fn second(base: i32) -&gt; i32 { 2 * base }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// #[fixture(second(-3))]</span>
+<span class="doccomment">/// fn injected(first: i32, second: i32) -&gt; i32 { first * second }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// ## Partial Type Injection</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # use std::fmt::Debug;</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # pub fn i() -&gt; u32 {</span>
+<span class="doccomment">/// #     42</span>
+<span class="doccomment">/// # }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # pub fn j() -&gt; i32 {</span>
+<span class="doccomment">/// #     -42</span>
+<span class="doccomment">/// # }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// #[fixture(::default&lt;impl Iterator&lt;Item=(u32, i32)&gt;&gt;::partial_1&lt;impl Iterator&lt;Item=(I,i32)&gt;&gt;)]</span>
+<span class="doccomment">/// pub fn fx&lt;I, J&gt;(i: I, j: J) -&gt; impl Iterator&lt;Item=(I, J)&gt; {</span>
+<span class="doccomment">///     std::iter::once((i, j))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+
+<span class="attribute">#[<span class="ident">proc_macro_attribute</span>]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">fixture</span>(
+    <span class="ident">args</span>: <span class="ident">proc_macro::TokenStream</span>,
+    <span class="ident">input</span>: <span class="ident">proc_macro::TokenStream</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">proc_macro::TokenStream</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span>: <span class="ident">FixtureInfo</span> <span class="op">=</span> <span class="macro">parse_macro_input!</span>(<span class="ident">args</span> <span class="kw">as</span> <span class="ident">FixtureInfo</span>);
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">fixture</span> <span class="op">=</span> <span class="macro">parse_macro_input!</span>(<span class="ident">input</span> <span class="kw">as</span> <span class="ident">ItemFn</span>);
+
+    <span class="kw">let</span> <span class="ident">replace_result</span> <span class="op">=</span> <span class="ident">ReplaceFutureAttribute::replace</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">fixture</span>);
+    <span class="kw">let</span> <span class="ident">extend_result</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">fixture</span>);
+
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">error::fixture</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>, <span class="kw-2">&amp;</span><span class="ident">info</span>);
+
+    <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Err</span>(<span class="ident">attrs_errors</span>) <span class="op">=</span> <span class="ident">replace_result</span> {
+        <span class="ident">attrs_errors</span>.<span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">errors</span>);
+    }
+    <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Err</span>(<span class="ident">attrs_errors</span>) <span class="op">=</span> <span class="ident">extend_result</span> {
+        <span class="ident">attrs_errors</span>.<span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">errors</span>);
+    }
+
+    <span class="kw">if</span> <span class="ident">errors</span>.<span class="ident">is_empty</span>() {
+        <span class="ident">render::fixture</span>(<span class="ident">fixture</span>, <span class="ident">info</span>).<span class="ident">into</span>()
+    } <span class="kw">else</span> {
+        <span class="ident">errors</span>
+    }
+    .<span class="ident">into</span>()
+}
+
+<span class="doccomment">/// The attribute that you should use for your tests. Your</span>
+<span class="doccomment">/// annotated function&#39;s arguments can be</span>
+<span class="doccomment">/// [injected](attr.rstest.html#injecting-fixtures) with</span>
+<span class="doccomment">/// [`[fixture]`](macro@fixture)s, provided by</span>
+<span class="doccomment">/// [parametrized cases](attr.rstest.html#test-parametrized-cases)</span>
+<span class="doccomment">/// or by [value lists](attr.rstest.html#values-lists).</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// `rstest` attribute can be applied to _any_ function and you can costumize its</span>
+<span class="doccomment">/// parameters by using function and arguments attributes.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Your test function can use generics, `impl` or `dyn` and like any kind of rust tests:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// - return results</span>
+<span class="doccomment">/// - marked by `#[should_panic]` attribute</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If the test function is an [`async` function](#async) `rstest` will run all tests as `async`</span>
+<span class="doccomment">/// tests. You can use it just with `async-std` and you should include `attributes` in</span>
+<span class="doccomment">/// `async-std`&#39;s features.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// In your test function you can:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// - [injecting fixtures](#injecting-fixtures)</span>
+<span class="doccomment">/// - Generate [parametrized test cases](#test-parametrized-cases)</span>
+<span class="doccomment">/// - Generate tests for each combination of [value lists](#values-lists)</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Injecting Fixtures</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// The simplest case is write a test that can be injected with</span>
+<span class="doccomment">/// [`[fixture]`](macro@fixture)s. You can just declare all used fixtures by passing</span>
+<span class="doccomment">/// them as a function&#39;s arguments. This can help your test to be neat</span>
+<span class="doccomment">/// and make your dependecy clear.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn injected() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(injected: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// [`[rstest]`](macro@rstest) procedural macro will desugar it to something that isn&#39;t</span>
+<span class="doccomment">/// so far from</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// #[test]</span>
+<span class="doccomment">/// fn the_test() {</span>
+<span class="doccomment">///     let injected=injected();</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you want to use long and descriptive names for your fixture but prefer to use</span>
+<span class="doccomment">/// shorter names inside your tests you use rename feature described in </span>
+<span class="doccomment">/// [fixture rename](attr.fixture.html#rename):</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn long_and_boring_descriptive_name() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn the_test(#[from(long_and_boring_descriptive_name)] short: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, short)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Sometimes is useful to have some parametes in your fixtures but your test would</span>
+<span class="doccomment">/// override the fixture&#39;s default values in some cases. Like in</span>
+<span class="doccomment">/// [fixture partial injection](attr.fixture.html#partial-injection) you use `#[with]`</span>
+<span class="doccomment">/// attribute to indicate some fixture&#39;s arguments also in `rstest`.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # struct User(String, u8);</span>
+<span class="doccomment">/// # impl User { fn name(&amp;self) -&gt; &amp;str {&amp;self.0} }</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn user(</span>
+<span class="doccomment">///     #[default(&quot;Alice&quot;)] name: impl AsRef&lt;str&gt;,</span>
+<span class="doccomment">///     #[default(22)] age: u8</span>
+<span class="doccomment">/// ) -&gt; User { User(name.as_ref().to_owned(), age) }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn check_user(#[with(&quot;Bob&quot;)] user: User) {</span>
+<span class="doccomment">///     assert_eq(&quot;Bob&quot;, user.name())</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Test Parametrized Cases</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you would execute your test for a set of input data cases</span>
+<span class="doccomment">/// you can define the arguments to use and the cases list. Let see</span>
+<span class="doccomment">/// the classical Fibonacci example. In this case we would give the</span>
+<span class="doccomment">/// `input` value and the `expected` result for a set of cases to test.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::rstest;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(0, 0)]</span>
+<span class="doccomment">/// #[case(1, 1)]</span>
+<span class="doccomment">/// #[case(2, 1)]</span>
+<span class="doccomment">/// #[case(3, 2)]</span>
+<span class="doccomment">/// #[case(4, 3)]</span>
+<span class="doccomment">/// fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {</span>
+<span class="doccomment">///     assert_eq!(expected, fibonacci(input))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// fn fibonacci(input: u32) -&gt; u32 {</span>
+<span class="doccomment">///     match input {</span>
+<span class="doccomment">///         0 =&gt; 0,</span>
+<span class="doccomment">///         1 =&gt; 1,</span>
+<span class="doccomment">///         n =&gt; fibonacci(n - 2) + fibonacci(n - 1)</span>
+<span class="doccomment">///     }</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// `rstest` will produce 5 indipendent tests and not just one that</span>
+<span class="doccomment">/// check every case. Every test can fail indipendently and `cargo test`</span>
+<span class="doccomment">/// will give follow output:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```text</span>
+<span class="doccomment">/// running 5 tests</span>
+<span class="doccomment">/// test fibonacci_test::case_1 ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_2 ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_3 ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_4 ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_5 ... ok</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// The cases input values can be arbitrary Rust expresions that return the</span>
+<span class="doccomment">/// argument type.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::rstest;</span>
+<span class="doccomment">///  </span>
+<span class="doccomment">/// fn sum(a: usize, b: usize) -&gt; usize { a + b }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(&quot;foo&quot;, 3)]</span>
+<span class="doccomment">/// #[case(String::from(&quot;foo&quot;), 2 + 1)]</span>
+<span class="doccomment">/// #[case(format!(&quot;foo&quot;), sum(2, 1))]</span>
+<span class="doccomment">/// fn test_len(#[case] s: impl AsRef&lt;str&gt;,#[case] len: usize) {</span>
+<span class="doccomment">///     assert_eq!(s.as_ref().len(), len);</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ### Magic Conversion</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// You can use the magic conversion feature every time you would define a variable</span>
+<span class="doccomment">/// where its type define `FromStr` trait: test will parse the string to build the value.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::rstest;</span>
+<span class="doccomment">/// # use std::path::PathBuf;</span>
+<span class="doccomment">/// # fn count_words(path: PathBuf) -&gt; usize {0}</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(&quot;resources/empty&quot;, 0)]</span>
+<span class="doccomment">/// #[case(&quot;resources/divine_commedy&quot;, 101.698)]</span>
+<span class="doccomment">/// fn test_count_words(#[case] path: PathBuf, #[case] expected: usize) {</span>
+<span class="doccomment">///     assert_eq!(expected, count_words(path))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ### Optional case description</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Optionally you can give a _description_ to every case simple by follow `case`</span>
+<span class="doccomment">/// with `::my_case_description` where `my_case_description` should be a a valid</span>
+<span class="doccomment">/// Rust ident.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case::zero_base_case(0, 0)]</span>
+<span class="doccomment">/// #[case::one_base_case(1, 1)]</span>
+<span class="doccomment">/// #[case(2, 1)]</span>
+<span class="doccomment">/// #[case(3, 2)]</span>
+<span class="doccomment">/// fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {</span>
+<span class="doccomment">///     assert_eq!(expected, fibonacci(input))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # fn fibonacci(input: u32) -&gt; u32 {</span>
+<span class="doccomment">/// #     match input {</span>
+<span class="doccomment">/// #         0 =&gt; 0,</span>
+<span class="doccomment">/// #         1 =&gt; 1,</span>
+<span class="doccomment">/// #         n =&gt; fibonacci(n - 2) + fibonacci(n - 1)</span>
+<span class="doccomment">/// #     }</span>
+<span class="doccomment">/// # }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Outuput will be</span>
+<span class="doccomment">/// ```text</span>
+<span class="doccomment">/// running 4 tests</span>
+<span class="doccomment">/// test fibonacci_test::case_1_zero_base_case ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_2_one_base_case ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_3 ... ok</span>
+<span class="doccomment">/// test fibonacci_test::case_4 ... ok</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ### Use specific `case` attributes</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Every function&#39;s attributes that preceding a `#[case]` attribute will</span>
+<span class="doccomment">/// be used in this test case and all function&#39;s attributes that follow the</span>
+<span class="doccomment">/// last `#[case]` attribute will mark all test cases.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// This feature can be use to mark just some cases as `should_panic`</span>
+<span class="doccomment">/// and choose to have a fine grain on expected panic messages.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// In follow example we run 3 tests where the first pass without any</span>
+<span class="doccomment">/// panic, in the second we catch a panic but we don&#39;t care about the message</span>
+<span class="doccomment">/// and in the third one we also check the panic message.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::rstest;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case::no_panic(0)]</span>
+<span class="doccomment">/// #[should_panic]</span>
+<span class="doccomment">/// #[case::panic(1)]</span>
+<span class="doccomment">/// #[should_panic(expected=&quot;expected&quot;)]</span>
+<span class="doccomment">/// #[case::panic_with_message(2)]</span>
+<span class="doccomment">/// fn attribute_per_case(#[case] val: i32) {</span>
+<span class="doccomment">///     match val {</span>
+<span class="doccomment">///         0 =&gt; assert!(true),</span>
+<span class="doccomment">///         1 =&gt; panic!(&quot;No catch&quot;),</span>
+<span class="doccomment">///         2 =&gt; panic!(&quot;expected&quot;),</span>
+<span class="doccomment">///         _ =&gt; unreachable!(),</span>
+<span class="doccomment">///     }</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Output:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```text</span>
+<span class="doccomment">/// running 3 tests</span>
+<span class="doccomment">/// test attribute_per_case::case_1_no_panic ... ok</span>
+<span class="doccomment">/// test attribute_per_case::case_3_panic_with_message ... ok</span>
+<span class="doccomment">/// test attribute_per_case::case_2_panic ... ok</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// To mark all your tests as `#[should_panic]` use:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::rstest;</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(1)]</span>
+<span class="doccomment">/// #[case(2)]</span>
+<span class="doccomment">/// #[case(3)]</span>
+<span class="doccomment">/// #[should_panic]</span>
+<span class="doccomment">/// fn fail(#[case] v: u32) { assert_eq!(0, v) }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Values Lists</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Another useful way to write a test and execute it for some values</span>
+<span class="doccomment">/// is to use the values list syntax. This syntax can be usefull both</span>
+<span class="doccomment">/// for a plain list and for testing all combination of input arguments.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # fn is_valid(input: &amp;str) -&gt; bool { true }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn should_be_valid(</span>
+<span class="doccomment">///     #[values(&quot;Jhon&quot;, &quot;alice&quot;, &quot;My_Name&quot;, &quot;Zigy_2001&quot;)]</span>
+<span class="doccomment">///     input: &amp;str</span>
+<span class="doccomment">/// ) {</span>
+<span class="doccomment">///     assert!(is_valid(input))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// or</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # fn valid_user(name: &amp;str, age: u8) -&gt; bool { true }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn should_accept_all_corner_cases(</span>
+<span class="doccomment">///     #[values(&quot;J&quot;, &quot;A&quot;, &quot;A________________________________________21&quot;)]</span>
+<span class="doccomment">///     name: &amp;str,</span>
+<span class="doccomment">///     #[values(14, 100)]</span>
+<span class="doccomment">///     age: u8</span>
+<span class="doccomment">/// ) {</span>
+<span class="doccomment">///     assert!(valid_user(name, age))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// where `cargo test` output is</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```text</span>
+<span class="doccomment">/// running 6 tests</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_1::age_1 ... ok</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_3::age_1 ... ok</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_3::age_2 ... ok</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_2::age_1 ... ok</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_2::age_2 ... ok</span>
+<span class="doccomment">/// test should_accept_all_corner_cases::name_1::age_2 ... ok</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Also value list implements the magic conversion feature: every time the value type</span>
+<span class="doccomment">/// implements `FromStr` trait you can use a literal string to define it.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Use Parametrize definition in more tests</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you need to use a test list for more than one test you can use</span>
+<span class="doccomment">/// [`rstest_reuse`](https://crates.io/crates/rstest_reuse) crate.</span>
+<span class="doccomment">/// With this helper crate you can define a template and use it everywhere.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::rstest;</span>
+<span class="doccomment">/// # use std::net::SocketAddr;</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// fn given_port(#[values(&quot;1.2.3.4:8000&quot;, &quot;4.3.2.1:8000&quot;, &quot;127.0.0.1:8000&quot;)] addr: SocketAddr) {</span>
+<span class="doccomment">///     assert_eq(8000, addr.port())</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```rust,ignore</span>
+<span class="doccomment">/// use rstest::rstest;</span>
+<span class="doccomment">/// use rstest_reuse::{self, *};</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[template]</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(2, 2)]</span>
+<span class="doccomment">/// #[case(4/2, 2)]</span>
+<span class="doccomment">/// fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[apply(two_simple_cases)]</span>
+<span class="doccomment">/// fn it_works(#[case] a: u32,#[case] b: u32) {</span>
+<span class="doccomment">///     assert!(a == b);</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// See [`rstest_reuse`](https://crates.io/crates/rstest_reuse) for more dettails.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Async</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// `rstest` provides out of the box `async` support. Just mark your</span>
+<span class="doccomment">/// test function as `async` and it&#39;ll use `#[async-std::test]` to</span>
+<span class="doccomment">/// annotate it. This feature can be really useful to build async</span>
+<span class="doccomment">/// parametric tests using a tidy syntax:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">/// # async fn async_sum(a: u32, b: u32) -&gt; u32 { a + b }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(5, 2, 3)]</span>
+<span class="doccomment">/// #[should_panic]</span>
+<span class="doccomment">/// #[case(42, 40, 1)]</span>
+<span class="doccomment">/// async fn my_async_test(#[case] expected: u32, #[case] a: u32, #[case] b: u32) {</span>
+<span class="doccomment">///     assert_eq!(expected, async_sum(a, b).await);</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Currently only `async-std` is supported out of the box. But if you need to use</span>
+<span class="doccomment">/// another runtime that provide it&#39;s own test attribute (i.e. `tokio::test` or</span>
+<span class="doccomment">/// `actix_rt::test`) you can use it in your `async` test like described in</span>
+<span class="doccomment">/// [Inject Test Attribute](attr.rstest.html#inject-test-attribute).</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// To use this feature, you need to enable `attributes` in the `async-std`</span>
+<span class="doccomment">/// features list in your `Cargo.toml`:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```toml</span>
+<span class="doccomment">/// async-std = { version = &quot;1.5&quot;, features = [&quot;attributes&quot;] }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If your test input is an async value (fixture or test parameter) you can use `#[future]`</span>
+<span class="doccomment">/// attribute to remove `impl Future&lt;Output = T&gt;` boilerplate and just use `T`:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// async fn base() -&gt; u32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(21, async { 2 })]</span>
+<span class="doccomment">/// #[case(6, async { 7 })]</span>
+<span class="doccomment">/// async fn my_async_test(#[future] base: u32, #[case] expected: u32, #[future] #[case] div: u32) {</span>
+<span class="doccomment">///     assert_eq!(expected, base.await / div.await);</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Inject Test Attribute</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you would like to use another `test` attribute for your test you can simply</span>
+<span class="doccomment">/// indicate it in your test function&#39;s attributes. For instance if you want</span>
+<span class="doccomment">/// to test some async function with use `actix_rt::test` attribute you can just write:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">/// use actix_rt;</span>
+<span class="doccomment">/// use std::future::Future;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(2, async { 4 })]</span>
+<span class="doccomment">/// #[case(21, async { 42 })]</span>
+<span class="doccomment">/// #[actix_rt::test]</span>
+<span class="doccomment">/// async fn my_async_test(#[case] a: u32, #[case] #[future] result: u32) {</span>
+<span class="doccomment">///     assert_eq!(2 * a, result.await);</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// Just the attributes that ends with `test` (last path segment) can be injected:</span>
+<span class="doccomment">/// in this case the `#[actix_rt::test]` attribute will replace the standard `#[test]`</span>
+<span class="doccomment">/// attribute.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Putting all Together</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// All these features can be used together with a mixture of fixture variables,</span>
+<span class="doccomment">/// fixed cases and bunch of values. For instance, you might need two</span>
+<span class="doccomment">/// test cases which test for panics, one for a logged in user and one for a guest user.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```rust</span>
+<span class="doccomment">/// # enum User { Guest, Logged, }</span>
+<span class="doccomment">/// # impl User { fn logged(_n: &amp;str, _d: &amp;str, _w: &amp;str, _s: &amp;str) -&gt; Self { Self::Logged } }</span>
+<span class="doccomment">/// # struct Item {}</span>
+<span class="doccomment">/// # trait Repository { fn find_items(&amp;self, user: &amp;User, query: &amp;str) -&gt; Result&lt;Vec&lt;Item&gt;, String&gt; { Err(&quot;Invalid query error&quot;.to_owned()) } }</span>
+<span class="doccomment">/// # #[derive(Default)] struct InMemoryRepository {}</span>
+<span class="doccomment">/// # impl Repository for InMemoryRepository {}</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn repository() -&gt; InMemoryRepository {</span>
+<span class="doccomment">///     let mut r = InMemoryRepository::default();</span>
+<span class="doccomment">///     // fill repository with some data</span>
+<span class="doccomment">///     r</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn alice() -&gt; User {</span>
+<span class="doccomment">///     User::logged(&quot;Alice&quot;, &quot;2001-10-04&quot;, &quot;London&quot;, &quot;UK&quot;)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case::authed_user(alice())] // We can use `fixture` also as standard function</span>
+<span class="doccomment">/// #[case::guest(User::Guest)]   // We can give a name to every case : `guest` in this case</span>
+<span class="doccomment">/// #[should_panic(expected = &quot;Invalid query error&quot;)] // We whould test a panic</span>
+<span class="doccomment">/// fn should_be_invalid_query_error(</span>
+<span class="doccomment">///     repository: impl Repository,</span>
+<span class="doccomment">///     #[case] user: User,</span>
+<span class="doccomment">///     #[values(&quot;     &quot;, &quot;^%$#@!&quot;, &quot;....&quot;)]</span>
+<span class="doccomment">///     query: &amp;str</span>
+<span class="doccomment">/// ) {</span>
+<span class="doccomment">///     repository.find_items(&amp;user, query).unwrap();</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Trace Input Arguments</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Sometimes can be very helpful to print all test&#39;s input arguments. To</span>
+<span class="doccomment">/// do it you can use the `#[trace]` function attribute that you can apply</span>
+<span class="doccomment">/// to all cases or just to some of them.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// use rstest::*;</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn injected() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[trace]</span>
+<span class="doccomment">/// fn the_test(injected: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Will print an output like</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```bash</span>
+<span class="doccomment">/// Testing started at 14.12 ...</span>
+<span class="doccomment">/// ------------ TEST ARGUMENTS ------------</span>
+<span class="doccomment">/// injected = 42</span>
+<span class="doccomment">/// -------------- TEST START --------------</span>
+<span class="doccomment">///</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Expected :42</span>
+<span class="doccomment">/// Actual   :43</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// But</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[case(1)]</span>
+<span class="doccomment">/// #[trace]</span>
+<span class="doccomment">/// #[case(2)]</span>
+<span class="doccomment">/// fn the_test(#[case] v: i32) {</span>
+<span class="doccomment">///     assert_eq!(0, v)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// will trace just `case_2` input arguments.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// If you want to trace input arguments but skip some of them that don&#39;t</span>
+<span class="doccomment">/// implement the `Debug` trait, you can also use the</span>
+<span class="doccomment">/// `#[notrace]` argument attribute to skip them:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # struct Xyz;</span>
+<span class="doccomment">/// # struct NoSense;</span>
+<span class="doccomment">/// #[rstest]</span>
+<span class="doccomment">/// #[trace]</span>
+<span class="doccomment">/// fn the_test(injected: i32, #[notrace] xyz: Xyz, #[notrace] have_no_sense: NoSense) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # Old _compact_ syntax</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// `rstest` support also a syntax where all options and configuration can be write as</span>
+<span class="doccomment">/// `rstest` attribute arguments. This syntax is a little less verbose but make</span>
+<span class="doccomment">/// composition harder: for istance try to add some cases to a `rstest_reuse` template</span>
+<span class="doccomment">/// is really hard.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// So we&#39;ll continue to maintain the old syntax for a long time but we strongly encourage</span>
+<span class="doccomment">/// to switch your test in the new form.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// Anyway, here we recall this syntax and rewrite the previous example in the _compact_ form.</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```text</span>
+<span class="doccomment">/// rstest(</span>
+<span class="doccomment">///     arg_1,</span>
+<span class="doccomment">///     ...,</span>
+<span class="doccomment">///     arg_n[,]</span>
+<span class="doccomment">///     [::attribute_1[:: ... [::attribute_k]]]</span>
+<span class="doccomment">/// )</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// Where:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// - `arg_i` could be one of the follow</span>
+<span class="doccomment">///   - `ident` that match to one of function arguments for parametrized cases</span>
+<span class="doccomment">///   - `case[::description](v1, ..., vl)` a test case</span>
+<span class="doccomment">///   - `fixture(v1, ..., vl) [as argument_name]` where fixture is the injected</span>
+<span class="doccomment">/// fixture and argument_name (default use fixture) is one of function arguments</span>
+<span class="doccomment">/// that and `v1, ..., vl` is a partial list of fixture&#39;s arguments</span>
+<span class="doccomment">///   - `ident =&gt; [v1, ..., vl]` where `ident` is one of function arguments and</span>
+<span class="doccomment">/// `v1, ..., vl` is a list of values for ident</span>
+<span class="doccomment">/// - `attribute_j` a test attribute like `trace` or `notrace`</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Fixture Arguments</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # struct User(String, u8);</span>
+<span class="doccomment">/// # impl User { fn name(&amp;self) -&gt; &amp;str {&amp;self.0} }</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// # #[fixture]</span>
+<span class="doccomment">/// # fn user(</span>
+<span class="doccomment">/// #     #[default(&quot;Alice&quot;)] name: impl AsRef&lt;str&gt;,</span>
+<span class="doccomment">/// #     #[default(22)] age: u8</span>
+<span class="doccomment">/// # ) -&gt; User { User(name.as_ref().to_owned(), age) }</span>
+<span class="doccomment">/// #</span>
+<span class="doccomment">/// #[rstest(user(&quot;Bob&quot;))]</span>
+<span class="doccomment">/// fn check_user(user: User) {</span>
+<span class="doccomment">///     assert_eq(&quot;Bob&quot;, user.name())</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Fixture Rename</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[fixture]</span>
+<span class="doccomment">/// fn long_and_boring_descriptive_name() -&gt; i32 { 42 }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest(long_and_boring_descriptive_name as short)]</span>
+<span class="doccomment">/// fn the_test(short: i32) {</span>
+<span class="doccomment">///     assert_eq!(42, short)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Parametrized</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// #[rstest(input, expected,</span>
+<span class="doccomment">///     case::zero_base_case(0, 0),</span>
+<span class="doccomment">///     case::one_base_case(1, 1),</span>
+<span class="doccomment">///     case(2, 1),</span>
+<span class="doccomment">///     case(3, 2),</span>
+<span class="doccomment">///     #[should_panic]</span>
+<span class="doccomment">///     case(4, 42)</span>
+<span class="doccomment">/// )]</span>
+<span class="doccomment">/// fn fibonacci_test(input: u32, expected: u32) {</span>
+<span class="doccomment">///     assert_eq!(expected, fibonacci(input))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// # fn fibonacci(input: u32) -&gt; u32 {</span>
+<span class="doccomment">/// #     match input {</span>
+<span class="doccomment">/// #         0 =&gt; 0,</span>
+<span class="doccomment">/// #         1 =&gt; 1,</span>
+<span class="doccomment">/// #         n =&gt; fibonacci(n - 2) + fibonacci(n - 1)</span>
+<span class="doccomment">/// #     }</span>
+<span class="doccomment">/// # }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## Values Lists</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # fn is_valid(input: &amp;str) -&gt; bool { true }</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// #[rstest(</span>
+<span class="doccomment">///     input =&gt; [&quot;Jhon&quot;, &quot;alice&quot;, &quot;My_Name&quot;, &quot;Zigy_2001&quot;]</span>
+<span class="doccomment">/// )]</span>
+<span class="doccomment">/// fn should_be_valid(input: &amp;str) {</span>
+<span class="doccomment">///     assert!(is_valid(input))</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ## `trace` and `notrace`</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">/// # use rstest::*;</span>
+<span class="doccomment">/// # struct Xyz;</span>
+<span class="doccomment">/// # struct NoSense;</span>
+<span class="doccomment">/// #[rstest(::trace::notrace(xzy, have_no_sense))]</span>
+<span class="doccomment">/// fn the_test(injected: i32, xyz: Xyz, have_no_sense: NoSense) {</span>
+<span class="doccomment">///     assert_eq!(42, injected)</span>
+<span class="doccomment">/// }</span>
+<span class="doccomment">/// ```</span>
+<span class="doccomment">///</span>
+<span class="attribute">#[<span class="ident">proc_macro_attribute</span>]</span>
+<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">rstest</span>(
+    <span class="ident">args</span>: <span class="ident">proc_macro::TokenStream</span>,
+    <span class="ident">input</span>: <span class="ident">proc_macro::TokenStream</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">proc_macro::TokenStream</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">test</span> <span class="op">=</span> <span class="macro">parse_macro_input!</span>(<span class="ident">input</span> <span class="kw">as</span> <span class="ident">ItemFn</span>);
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="macro">parse_macro_input!</span>(<span class="ident">args</span> <span class="kw">as</span> <span class="ident">RsTestInfo</span>);
+
+    <span class="kw">let</span> <span class="ident">replace_result</span> <span class="op">=</span> <span class="ident">ReplaceFutureAttribute::replace</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">test</span>);
+    <span class="kw">let</span> <span class="ident">extend_result</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">test</span>);
+
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">error::rstest</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>, <span class="kw-2">&amp;</span><span class="ident">info</span>);
+
+    <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Err</span>(<span class="ident">attrs_errors</span>) <span class="op">=</span> <span class="ident">replace_result</span> {
+        <span class="ident">attrs_errors</span>.<span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">errors</span>);
+    }
+    <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Err</span>(<span class="ident">attrs_errors</span>) <span class="op">=</span> <span class="ident">extend_result</span> {
+        <span class="ident">attrs_errors</span>.<span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">errors</span>);
+    }
+
+    <span class="kw">if</span> <span class="ident">errors</span>.<span class="ident">is_empty</span>() {
+        <span class="kw">if</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">has_list_values</span>() {
+            <span class="ident">render::matrix</span>(<span class="ident">test</span>, <span class="ident">info</span>)
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">has_cases</span>() {
+            <span class="ident">render::parametrize</span>(<span class="ident">test</span>, <span class="ident">info</span>)
+        } <span class="kw">else</span> {
+            <span class="ident">render::single</span>(<span class="ident">test</span>, <span class="ident">info</span>)
+        }
+    } <span class="kw">else</span> {
+        <span class="ident">errors</span>
+    }
+    .<span class="ident">into</span>()
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../" data-current-crate="rstest" data-search-index-js="../../search-index.js" data-search-js="../../search.js"></div>
+    <script src="../../main.js"></script><script src="../../source-script.js"></script><script src="../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/fixture.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/fixture.rs.html
new file mode 100644
index 0000000..065c3d2
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/fixture.rs.html
@@ -0,0 +1,1311 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/fixture.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>fixture.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+<span id="370">370</span>
+<span id="371">371</span>
+<span id="372">372</span>
+<span id="373">373</span>
+<span id="374">374</span>
+<span id="375">375</span>
+<span id="376">376</span>
+<span id="377">377</span>
+<span id="378">378</span>
+<span id="379">379</span>
+<span id="380">380</span>
+<span id="381">381</span>
+<span id="382">382</span>
+<span id="383">383</span>
+<span id="384">384</span>
+<span id="385">385</span>
+<span id="386">386</span>
+<span id="387">387</span>
+<span id="388">388</span>
+<span id="389">389</span>
+<span id="390">390</span>
+<span id="391">391</span>
+<span id="392">392</span>
+<span id="393">393</span>
+<span id="394">394</span>
+<span id="395">395</span>
+<span id="396">396</span>
+<span id="397">397</span>
+<span id="398">398</span>
+<span id="399">399</span>
+<span id="400">400</span>
+<span id="401">401</span>
+<span id="402">402</span>
+<span id="403">403</span>
+<span id="404">404</span>
+<span id="405">405</span>
+<span id="406">406</span>
+<span id="407">407</span>
+<span id="408">408</span>
+<span id="409">409</span>
+<span id="410">410</span>
+<span id="411">411</span>
+<span id="412">412</span>
+<span id="413">413</span>
+<span id="414">414</span>
+<span id="415">415</span>
+<span id="416">416</span>
+<span id="417">417</span>
+<span id="418">418</span>
+<span id="419">419</span>
+<span id="420">420</span>
+<span id="421">421</span>
+<span id="422">422</span>
+<span id="423">423</span>
+<span id="424">424</span>
+<span id="425">425</span>
+<span id="426">426</span>
+<span id="427">427</span>
+<span id="428">428</span>
+<span id="429">429</span>
+<span id="430">430</span>
+<span id="431">431</span>
+<span id="432">432</span>
+<span id="433">433</span>
+<span id="434">434</span>
+<span id="435">435</span>
+<span id="436">436</span>
+<span id="437">437</span>
+<span id="438">438</span>
+<span id="439">439</span>
+<span id="440">440</span>
+<span id="441">441</span>
+<span id="442">442</span>
+<span id="443">443</span>
+<span id="444">444</span>
+<span id="445">445</span>
+<span id="446">446</span>
+<span id="447">447</span>
+<span id="448">448</span>
+<span id="449">449</span>
+<span id="450">450</span>
+<span id="451">451</span>
+<span id="452">452</span>
+<span id="453">453</span>
+<span id="454">454</span>
+<span id="455">455</span>
+<span id="456">456</span>
+<span id="457">457</span>
+<span id="458">458</span>
+<span id="459">459</span>
+<span id="460">460</span>
+<span id="461">461</span>
+<span id="462">462</span>
+<span id="463">463</span>
+<span id="464">464</span>
+<span id="465">465</span>
+<span id="466">466</span>
+<span id="467">467</span>
+<span id="468">468</span>
+<span id="469">469</span>
+<span id="470">470</span>
+<span id="471">471</span>
+<span id="472">472</span>
+<span id="473">473</span>
+<span id="474">474</span>
+<span id="475">475</span>
+<span id="476">476</span>
+<span id="477">477</span>
+<span id="478">478</span>
+<span id="479">479</span>
+<span id="480">480</span>
+<span id="481">481</span>
+<span id="482">482</span>
+<span id="483">483</span>
+<span id="484">484</span>
+<span id="485">485</span>
+<span id="486">486</span>
+<span id="487">487</span>
+<span id="488">488</span>
+<span id="489">489</span>
+<span id="490">490</span>
+<span id="491">491</span>
+<span id="492">492</span>
+<span id="493">493</span>
+<span id="494">494</span>
+<span id="495">495</span>
+<span id="496">496</span>
+<span id="497">497</span>
+<span id="498">498</span>
+<span id="499">499</span>
+<span id="500">500</span>
+<span id="501">501</span>
+<span id="502">502</span>
+<span id="503">503</span>
+<span id="504">504</span>
+<span id="505">505</span>
+<span id="506">506</span>
+<span id="507">507</span>
+<span id="508">508</span>
+<span id="509">509</span>
+<span id="510">510</span>
+<span id="511">511</span>
+<span id="512">512</span>
+<span id="513">513</span>
+<span id="514">514</span>
+<span id="515">515</span>
+<span id="516">516</span>
+<span id="517">517</span>
+<span id="518">518</span>
+<span id="519">519</span>
+<span id="520">520</span>
+<span id="521">521</span>
+<span id="522">522</span>
+<span id="523">523</span>
+<span id="524">524</span>
+<span id="525">525</span>
+<span id="526">526</span>
+<span id="527">527</span>
+<span id="528">528</span>
+<span id="529">529</span>
+<span id="530">530</span>
+<span id="531">531</span>
+<span id="532">532</span>
+<span id="533">533</span>
+<span id="534">534</span>
+<span id="535">535</span>
+<span id="536">536</span>
+<span id="537">537</span>
+<span id="538">538</span>
+<span id="539">539</span>
+<span id="540">540</span>
+<span id="541">541</span>
+<span id="542">542</span>
+<span id="543">543</span>
+<span id="544">544</span>
+<span id="545">545</span>
+<span id="546">546</span>
+<span id="547">547</span>
+<span id="548">548</span>
+<span id="549">549</span>
+<span id="550">550</span>
+<span id="551">551</span>
+<span id="552">552</span>
+<span id="553">553</span>
+<span id="554">554</span>
+<span id="555">555</span>
+<span id="556">556</span>
+<span id="557">557</span>
+<span id="558">558</span>
+<span id="559">559</span>
+<span id="560">560</span>
+<span id="561">561</span>
+<span id="562">562</span>
+<span id="563">563</span>
+<span id="564">564</span>
+<span id="565">565</span>
+<span id="566">566</span>
+<span id="567">567</span>
+<span id="568">568</span>
+<span id="569">569</span>
+<span id="570">570</span>
+<span id="571">571</span>
+<span id="572">572</span>
+<span id="573">573</span>
+<span id="574">574</span>
+<span id="575">575</span>
+<span id="576">576</span>
+<span id="577">577</span>
+<span id="578">578</span>
+<span id="579">579</span>
+<span id="580">580</span>
+<span id="581">581</span>
+<span id="582">582</span>
+<span id="583">583</span>
+<span id="584">584</span>
+<span id="585">585</span>
+<span id="586">586</span>
+<span id="587">587</span>
+<span id="588">588</span>
+<span id="589">589</span>
+<span id="590">590</span>
+<span id="591">591</span>
+<span id="592">592</span>
+<span id="593">593</span>
+<span id="594">594</span>
+<span id="595">595</span>
+<span id="596">596</span>
+<span id="597">597</span>
+<span id="598">598</span>
+<span id="599">599</span>
+<span id="600">600</span>
+<span id="601">601</span>
+<span id="602">602</span>
+<span id="603">603</span>
+<span id="604">604</span>
+<span id="605">605</span>
+<span id="606">606</span>
+<span id="607">607</span>
+<span id="608">608</span>
+<span id="609">609</span>
+<span id="610">610</span>
+<span id="611">611</span>
+<span id="612">612</span>
+<span id="613">613</span>
+<span id="614">614</span>
+<span id="615">615</span>
+<span id="616">616</span>
+<span id="617">617</span>
+<span id="618">618</span>
+<span id="619">619</span>
+<span id="620">620</span>
+<span id="621">621</span>
+<span id="622">622</span>
+<span id="623">623</span>
+<span id="624">624</span>
+<span id="625">625</span>
+<span id="626">626</span>
+<span id="627">627</span>
+<span id="628">628</span>
+<span id="629">629</span>
+<span id="630">630</span>
+<span id="631">631</span>
+<span id="632">632</span>
+<span id="633">633</span>
+<span id="634">634</span>
+<span id="635">635</span>
+<span id="636">636</span>
+<span id="637">637</span>
+<span id="638">638</span>
+<span id="639">639</span>
+<span id="640">640</span>
+<span id="641">641</span>
+<span id="642">642</span>
+<span id="643">643</span>
+<span id="644">644</span>
+<span id="645">645</span>
+<span id="646">646</span>
+<span id="647">647</span>
+<span id="648">648</span>
+<span id="649">649</span>
+<span id="650">650</span>
+<span id="651">651</span>
+<span id="652">652</span>
+</pre><pre class="rust">
+<span class="doccomment">/// `fixture`&#39;s related data and parsing</span>
+<span class="kw">use</span> <span class="ident">syn</span>::{
+    <span class="ident">parse</span>::{<span class="ident">Parse</span>, <span class="ident">ParseStream</span>},
+    <span class="ident">parse_quote</span>,
+    <span class="ident">visit_mut::VisitMut</span>,
+    <span class="ident">Expr</span>, <span class="ident">FnArg</span>, <span class="ident">Ident</span>, <span class="ident">ItemFn</span>, <span class="ident">Token</span>,
+};
+
+<span class="kw">use</span> <span class="kw">super</span>::{
+    <span class="ident">extract_argument_attrs</span>, <span class="ident">extract_default_return_type</span>, <span class="ident">extract_defaults</span>, <span class="ident">extract_fixtures</span>,
+    <span class="ident">extract_partials_return_type</span>, <span class="ident">parse_vector_trailing_till_double_comma</span>, <span class="ident">Attributes</span>,
+    <span class="ident">ExtendWithFunctionAttrs</span>, <span class="ident">Fixture</span>,
+};
+<span class="kw">use</span> <span class="kw">crate</span>::{<span class="ident">error::ErrorsVec</span>, <span class="ident">refident::RefIdent</span>, <span class="ident">utils::attr_is</span>};
+<span class="kw">use</span> <span class="kw">crate</span>::{<span class="ident">parse::Attribute</span>, <span class="ident">utils::attr_in</span>};
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">quote</span>::{<span class="ident">format_ident</span>, <span class="ident">ToTokens</span>};
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">FixtureInfo</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">data</span>: <span class="ident">FixtureData</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">attributes</span>: <span class="ident">FixtureModifiers</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">FixtureModifiers</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Attributes</span><span class="op">&gt;</span>()<span class="question-mark">?</span>.<span class="ident">into</span>())
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">FixtureInfo</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Ok</span>(<span class="kw">if</span> <span class="ident">input</span>.<span class="ident">is_empty</span>() {
+            <span class="ident">Default::default</span>()
+        } <span class="kw">else</span> {
+            <span class="self">Self</span> {
+                <span class="ident">data</span>: <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>,
+                <span class="ident">attributes</span>: <span class="ident">input</span>
+                    .<span class="ident">parse</span>::<span class="op">&lt;</span><span class="macro">Token!</span>[::]<span class="op">&gt;</span>()
+                    .<span class="ident">or_else</span>(<span class="op">|</span><span class="kw">_</span><span class="op">|</span> <span class="prelude-val">Ok</span>(<span class="ident">Default::default</span>()))
+                    .<span class="ident">and_then</span>(<span class="op">|</span><span class="kw">_</span><span class="op">|</span> <span class="ident">input</span>.<span class="ident">parse</span>())<span class="question-mark">?</span>,
+            }
+        })
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ExtendWithFunctionAttrs</span> <span class="kw">for</span> <span class="ident">FixtureInfo</span> {
+    <span class="kw">fn</span> <span class="ident">extend_with_function_attrs</span>(
+        <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>,
+        <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>,
+    ) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">std::result::Result</span><span class="op">&lt;</span>(), <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="macro">composed_tuple!</span>(
+            <span class="ident">fixtures</span>,
+            <span class="ident">defaults</span>,
+            <span class="ident">default_return_type</span>,
+            <span class="ident">partials_return_type</span>
+        ) <span class="op">=</span> <span class="macro">merge_errors!</span>(
+            <span class="ident">extract_fixtures</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_defaults</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_default_return_type</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_partials_return_type</span>(<span class="ident">item_fn</span>)
+        )<span class="question-mark">?</span>;
+        <span class="self">self</span>.<span class="ident">data</span>.<span class="ident">items</span>.<span class="ident">extend</span>(
+            <span class="ident">fixtures</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">into</span>())
+                .<span class="ident">chain</span>(<span class="ident">defaults</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">d</span><span class="op">|</span> <span class="ident">d</span>.<span class="ident">into</span>())),
+        );
+        <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Some</span>(<span class="ident">return_type</span>) <span class="op">=</span> <span class="ident">default_return_type</span> {
+            <span class="self">self</span>.<span class="ident">attributes</span>.<span class="ident">set_default_return_type</span>(<span class="ident">return_type</span>);
+        }
+        <span class="kw">for</span> (<span class="ident">id</span>, <span class="ident">return_type</span>) <span class="kw">in</span> <span class="ident">partials_return_type</span> {
+            <span class="self">self</span>.<span class="ident">attributes</span>.<span class="ident">set_partial_return_type</span>(<span class="ident">id</span>, <span class="ident">return_type</span>);
+        }
+        <span class="prelude-val">Ok</span>(())
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">parse_attribute_args_just_once</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span>, <span class="ident">T</span>: <span class="ident">Parse</span><span class="op">&gt;</span>(
+    <span class="ident">attributes</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">syn::Attribute</span><span class="op">&gt;</span>,
+    <span class="ident">name</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>,
+) <span class="op">-</span><span class="op">&gt;</span> (<span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">T</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>) {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">Vec::new</span>();
+    <span class="kw">let</span> <span class="ident">val</span> <span class="op">=</span> <span class="ident">attributes</span>
+        .<span class="ident">filter</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="ident">name</span>))
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> (<span class="ident">a</span>, <span class="ident">a</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">T</span><span class="op">&gt;</span>()))
+        .<span class="ident">fold</span>(<span class="prelude-val">None</span>, <span class="op">|</span><span class="ident">first</span>, (<span class="ident">a</span>, <span class="ident">res</span>)<span class="op">|</span> <span class="kw">match</span> (<span class="ident">first</span>, <span class="ident">res</span>) {
+            (<span class="prelude-val">None</span>, <span class="prelude-val">Ok</span>(<span class="ident">parsed</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">parsed</span>),
+            (<span class="ident">first</span>, <span class="prelude-val">Err</span>(<span class="ident">err</span>)) <span class="op">=</span><span class="op">&gt;</span> {
+                <span class="ident">errors</span>.<span class="ident">push</span>(<span class="ident">err</span>);
+                <span class="ident">first</span>
+            }
+            (<span class="ident">first</span>, <span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> {
+                <span class="ident">errors</span>.<span class="ident">push</span>(<span class="ident">syn::Error::new_spanned</span>(
+                    <span class="ident">a</span>,
+                    <span class="macro">format!</span>(
+                        <span class="string">&quot;You cannot use &#39;{}&#39; attribute more than once for the same argument&quot;</span>,
+                        <span class="ident">name</span>
+                    ),
+                ));
+                <span class="ident">first</span>
+            }
+        });
+    (<span class="ident">val</span>, <span class="ident">errors</span>)
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract Fixtures and</span>
+<span class="doccomment">/// eventualy parsing errors</span>
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">FixturesFunctionExtractor</span>(<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span>, <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">FixturesFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_fn_arg_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>) {
+        <span class="kw">if</span> <span class="kw">let</span> <span class="ident">FnArg::Typed</span>(<span class="kw-2">ref</span> <span class="kw-2">mut</span> <span class="ident">arg</span>) <span class="op">=</span> <span class="ident">node</span> {
+            <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="kw">match</span> <span class="ident">arg</span>.<span class="ident">pat</span>.<span class="ident">as_ref</span>() {
+                <span class="ident">syn::Pat::Ident</span>(<span class="ident">ident</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">ident</span>.<span class="ident">ident</span>.<span class="ident">clone</span>(),
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="kw">return</span>,
+            };
+            <span class="kw">let</span> (<span class="ident">extracted</span>, <span class="ident">remain</span>): (<span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>) <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">arg</span>.<span class="ident">attrs</span>)
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">partition</span>(<span class="op">|</span><span class="ident">attr</span><span class="op">|</span> <span class="ident">attr_in</span>(<span class="ident">attr</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;with&quot;</span>, <span class="string">&quot;from&quot;</span>]));
+            <span class="ident">arg</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="ident">remain</span>;
+
+            <span class="kw">let</span> (<span class="ident">pos</span>, <span class="ident">errors</span>) <span class="op">=</span> <span class="ident">parse_attribute_args_just_once</span>(<span class="ident">extracted</span>.<span class="ident">iter</span>(), <span class="string">&quot;with&quot;</span>);
+            <span class="self">self</span>.<span class="number">1</span>.<span class="ident">extend</span>(<span class="ident">errors</span>.<span class="ident">into_iter</span>());
+            <span class="kw">let</span> (<span class="ident">resolve</span>, <span class="ident">errors</span>) <span class="op">=</span> <span class="ident">parse_attribute_args_just_once</span>(<span class="ident">extracted</span>.<span class="ident">iter</span>(), <span class="string">&quot;from&quot;</span>);
+            <span class="self">self</span>.<span class="number">1</span>.<span class="ident">extend</span>(<span class="ident">errors</span>.<span class="ident">into_iter</span>());
+            <span class="kw">if</span> <span class="ident">pos</span>.<span class="ident">is_some</span>() <span class="op">|</span><span class="op">|</span> <span class="ident">resolve</span>.<span class="ident">is_some</span>() {
+                <span class="self">self</span>.<span class="number">0</span>
+                    .<span class="ident">push</span>(<span class="ident">Fixture::new</span>(<span class="ident">name</span>, <span class="ident">resolve</span>, <span class="ident">pos</span>.<span class="ident">unwrap_or_default</span>()))
+            }
+        }
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract fixture default values info and</span>
+<span class="doccomment">/// eventualy parsing errors</span>
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">DefaultsFunctionExtractor</span>(
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">ArgumentValue</span><span class="op">&gt;</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>,
+);
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">DefaultsFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_fn_arg_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>) {
+        <span class="kw">for</span> <span class="ident">r</span> <span class="kw">in</span> <span class="ident">extract_argument_attrs</span>(
+            <span class="ident">node</span>,
+            <span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="string">&quot;default&quot;</span>),
+            <span class="op">|</span><span class="ident">a</span>, <span class="ident">name</span><span class="op">|</span> {
+                <span class="ident">a</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span>()
+                    .<span class="ident">map</span>(<span class="op">|</span><span class="ident">e</span><span class="op">|</span> <span class="ident">ArgumentValue::new</span>(<span class="ident">name</span>.<span class="ident">clone</span>(), <span class="ident">e</span>))
+            },
+        ) {
+            <span class="kw">match</span> <span class="ident">r</span> {
+                <span class="prelude-val">Ok</span>(<span class="ident">value</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">0</span>.<span class="ident">push</span>(<span class="ident">value</span>),
+                <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">1</span>.<span class="ident">push</span>(<span class="ident">err</span>),
+            }
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">FixtureData</span> {
+    <span class="kw">pub</span> <span class="ident">items</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">FixtureItem</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">FixtureData</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fixtures</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">Fixture</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">f</span> {
+            <span class="ident">FixtureItem::Fixture</span>(<span class="kw-2">ref</span> <span class="ident">fixture</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">fixture</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">values</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">ArgumentValue</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">f</span> {
+            <span class="ident">FixtureItem::ArgumentValue</span>(<span class="kw-2">ref</span> <span class="ident">value</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">value</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">FixtureData</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[::]) {
+            <span class="prelude-val">Ok</span>(<span class="ident">Default::default</span>())
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="self">Self</span> {
+                <span class="ident">items</span>: <span class="ident">parse_vector_trailing_till_double_comma</span>::<span class="op">&lt;</span><span class="kw">_</span>, <span class="macro">Token!</span>[,]<span class="op">&gt;</span>(<span class="ident">input</span>)<span class="question-mark">?</span>,
+            })
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">ArgumentValue</span> {
+    <span class="kw">pub</span> <span class="ident">name</span>: <span class="ident">Ident</span>,
+    <span class="kw">pub</span> <span class="ident">expr</span>: <span class="ident">Expr</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">ArgumentValue</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">new</span>(<span class="ident">name</span>: <span class="ident">Ident</span>, <span class="ident">expr</span>: <span class="ident">Expr</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span> { <span class="ident">name</span>, <span class="ident">expr</span> }
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">enum</span> <span class="ident">FixtureItem</span> {
+    <span class="ident">Fixture</span>(<span class="ident">Fixture</span>),
+    <span class="ident">ArgumentValue</span>(<span class="ident">ArgumentValue</span>),
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">FixtureItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">f</span>: <span class="ident">Fixture</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">FixtureItem::Fixture</span>(<span class="ident">f</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">FixtureItem</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek2</span>(<span class="macro">Token!</span>[<span class="op">=</span>]) {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">ArgumentValue</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">v</span><span class="op">|</span> <span class="ident">v</span>.<span class="ident">into</span>())
+        } <span class="kw">else</span> {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">v</span><span class="op">|</span> <span class="ident">v</span>.<span class="ident">into</span>())
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">RefIdent</span> <span class="kw">for</span> <span class="ident">FixtureItem</span> {
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">FixtureItem::Fixture</span>(<span class="ident">Fixture</span> { <span class="kw-2">ref</span> <span class="ident">name</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">name</span>,
+            <span class="ident">FixtureItem::ArgumentValue</span>(<span class="ident">ArgumentValue</span> { <span class="kw-2">ref</span> <span class="ident">name</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">name</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ToTokens</span> <span class="kw">for</span> <span class="ident">FixtureItem</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="self">self</span>.<span class="ident">ident</span>().<span class="ident">to_tokens</span>(<span class="ident">tokens</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">ArgumentValue</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">FixtureItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">av</span>: <span class="ident">ArgumentValue</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">FixtureItem::ArgumentValue</span>(<span class="ident">av</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">ArgumentValue</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">let</span> <span class="ident">_eq</span>: <span class="macro">Token!</span>[<span class="op">=</span>] <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">let</span> <span class="ident">expr</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="prelude-val">Ok</span>(<span class="ident">ArgumentValue::new</span>(<span class="ident">name</span>, <span class="ident">expr</span>))
+    }
+}
+
+<span class="macro">wrap_attributes!</span>(<span class="ident">FixtureModifiers</span>);
+
+<span class="kw">impl</span> <span class="ident">FixtureModifiers</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">const</span> <span class="ident">DEFAULT_RET_ATTR</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;static</span> <span class="ident">str</span> <span class="op">=</span> <span class="string">&quot;default&quot;</span>;
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">const</span> <span class="ident">PARTIAL_RET_ATTR</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;static</span> <span class="ident">str</span> <span class="op">=</span> <span class="string">&quot;partial_&quot;</span>;
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_default_type</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::ReturnType</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">extract_type</span>(<span class="self">Self</span><span class="ident">::DEFAULT_RET_ATTR</span>)
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_partial_type</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">pos</span>: <span class="ident">usize</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::ReturnType</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">extract_type</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(<span class="string">&quot;{}{}&quot;</span>, <span class="self">Self</span><span class="ident">::PARTIAL_RET_ATTR</span>, <span class="ident">pos</span>))
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">set_default_return_type</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">return_type</span>: <span class="ident">syn::Type</span>) {
+        <span class="self">self</span>.<span class="ident">inner</span>.<span class="ident">attributes</span>.<span class="ident">push</span>(<span class="ident">Attribute::Type</span>(
+            <span class="macro">format_ident!</span>(<span class="string">&quot;{}&quot;</span>, <span class="self">Self</span><span class="ident">::DEFAULT_RET_ATTR</span>),
+            <span class="ident">return_type</span>,
+        ))
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">set_partial_return_type</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">id</span>: <span class="ident">usize</span>, <span class="ident">return_type</span>: <span class="ident">syn::Type</span>) {
+        <span class="self">self</span>.<span class="ident">inner</span>.<span class="ident">attributes</span>.<span class="ident">push</span>(<span class="ident">Attribute::Type</span>(
+            <span class="macro">format_ident!</span>(<span class="string">&quot;{}{}&quot;</span>, <span class="self">Self</span><span class="ident">::PARTIAL_RET_ATTR</span>, <span class="ident">id</span>),
+            <span class="ident">return_type</span>,
+        ))
+    }
+
+    <span class="kw">fn</span> <span class="ident">extract_type</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">attr_name</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::ReturnType</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">iter</span>()
+            .<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">m</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">m</span> {
+                <span class="ident">Attribute::Type</span>(<span class="ident">name</span>, <span class="ident">t</span>) <span class="kw">if</span> <span class="ident">name</span> <span class="op">=</span><span class="op">=</span> <span class="ident">attr_name</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="macro">parse_quote!</span> { <span class="op">-</span><span class="op">&gt;</span> #<span class="ident">t</span>}),
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+            })
+            .<span class="ident">next</span>()
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+    <span class="kw">mod</span> <span class="ident">parse</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+        <span class="kw">use</span> <span class="ident">mytest::rstest</span>;
+
+        <span class="kw">fn</span> <span class="ident">parse_fixture</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">fixture_data</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">FixtureInfo</span> {
+            <span class="ident">parse_meta</span>(<span class="ident">fixture_data</span>)
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">happy_path</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_fixture</span>(
+                <span class="string">r#&quot;my_fixture(42, &quot;other&quot;), other(vec![42]), value=42, other_value=vec![1.0]
+                    :: trace :: no_trace(some)&quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[
+                    <span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]).<span class="ident">into</span>(),
+                    <span class="ident">fixture</span>(<span class="string">&quot;other&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;vec![42]&quot;</span>]).<span class="ident">into</span>(),
+                    <span class="ident">arg_value</span>(<span class="string">&quot;value&quot;</span>, <span class="string">&quot;42&quot;</span>).<span class="ident">into</span>(),
+                    <span class="ident">arg_value</span>(<span class="string">&quot;other_value&quot;</span>, <span class="string">&quot;vec![1.0]&quot;</span>).<span class="ident">into</span>(),
+                ]
+                .<span class="ident">into</span>(),
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[
+                        <span class="ident">Attribute::attr</span>(<span class="string">&quot;trace&quot;</span>),
+                        <span class="ident">Attribute::tagged</span>(<span class="string">&quot;no_trace&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;some&quot;</span>]),
+                    ],
+                }
+                .<span class="ident">into</span>(),
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">some_literals</span>() {
+            <span class="kw">let</span> <span class="ident">args_expressions</span> <span class="op">=</span> <span class="ident">literal_expressions_str</span>();
+            <span class="kw">let</span> <span class="ident">fixture</span> <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(<span class="string">&quot;my_fixture({})&quot;</span>, <span class="ident">args_expressions</span>.<span class="ident">join</span>(<span class="string">&quot;, &quot;</span>)));
+            <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">fixture</span>.<span class="ident">data</span>.<span class="ident">fixtures</span>().<span class="ident">next</span>().<span class="ident">unwrap</span>().<span class="ident">positional</span>.<span class="ident">clone</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>(<span class="ident">args_expressions</span>), <span class="ident">args</span>.<span class="number">0</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">empty_fixtures</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="string">r#&quot;::trace::no_trace(some)&quot;#</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[
+                        <span class="ident">Attribute::attr</span>(<span class="string">&quot;trace&quot;</span>),
+                        <span class="ident">Attribute::tagged</span>(<span class="string">&quot;no_trace&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;some&quot;</span>]),
+                    ],
+                }
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">empty_attributes</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="string">r#&quot;my_fixture(42, &quot;other&quot;)&quot;#</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]).<span class="ident">into</span>()].<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">rstest</span>]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;first(42),&quot;</span>, <span class="number">1</span>)]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="string">&quot;first(42), second=42,&quot;</span>, <span class="number">2</span>)]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="string">r#&quot;fixture(42, &quot;other&quot;), :: trace&quot;#</span>, <span class="number">1</span>)]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="string">r#&quot;second=42, fixture(42, &quot;other&quot;), :: trace&quot;#</span>, <span class="number">2</span>)]</span>
+        <span class="kw">fn</span> <span class="ident">should_accept_trailing_comma</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">input</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">usize</span>) {
+            <span class="kw">let</span> <span class="ident">info</span>: <span class="ident">FixtureInfo</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">ast</span>();
+
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">expected</span>,
+                <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">fixtures</span>().<span class="ident">count</span>() <span class="op">+</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">values</span>().<span class="ident">count</span>()
+            );
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">extend</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+    <span class="kw">use</span> <span class="ident">syn::ItemFn</span>;
+
+    <span class="kw">mod</span> <span class="ident">should</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">use_with_attributes</span>() {
+            <span class="kw">let</span> <span class="ident">to_parse</span> <span class="op">=</span> <span class="string">r#&quot;
+                fn my_fix(#[with(2)] f1: &amp;str, #[with(vec![1,2], &quot;s&quot;)] f2: u32) {}
+            &quot;#</span>;
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="ident">to_parse</span>.<span class="ident">ast</span>();
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+            <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[
+                    <span class="ident">fixture</span>(<span class="string">&quot;f1&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;2&quot;</span>]).<span class="ident">into</span>(),
+                    <span class="ident">fixture</span>(<span class="string">&quot;f2&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;vec![1,2]&quot;</span>, <span class="string">r#&quot;&quot;s&quot;&quot;#</span>]).<span class="ident">into</span>(),
+                ]
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert!</span>(<span class="op">!</span><span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">item_fn</span>).<span class="ident">contains</span>(<span class="string">&quot;with&quot;</span>));
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">info</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">rename_with_attributes</span>() {
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    fn test_fn(
+                        #[from(long_fixture_name)] 
+                        #[with(42, &quot;other&quot;)] short: u32, 
+                        #[from(simple)]
+                        s: &amp;str,
+                        no_change: i32) {
+                    }
+                    &quot;#</span>
+            .<span class="ident">ast</span>();
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[
+                    <span class="ident">fixture</span>(<span class="string">&quot;short&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>])
+                        .<span class="ident">with_resolve</span>(<span class="string">&quot;long_fixture_name&quot;</span>)
+                        .<span class="ident">into</span>(),
+                    <span class="ident">fixture</span>(<span class="string">&quot;s&quot;</span>, <span class="kw-2">&amp;</span>[]).<span class="ident">with_resolve</span>(<span class="string">&quot;simple&quot;</span>).<span class="ident">into</span>(),
+                ]
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+            <span class="ident">data</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">use_default_values_attributes</span>() {
+            <span class="kw">let</span> <span class="ident">to_parse</span> <span class="op">=</span> <span class="string">r#&quot;
+                fn my_fix(#[default(2)] f1: &amp;str, #[default((vec![1,2], &quot;s&quot;))] f2: (Vec&lt;u32&gt;, &amp;str)) {}
+            &quot;#</span>;
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="ident">to_parse</span>.<span class="ident">ast</span>();
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+            <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">FixtureInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[
+                    <span class="ident">arg_value</span>(<span class="string">&quot;f1&quot;</span>, <span class="string">&quot;2&quot;</span>).<span class="ident">into</span>(),
+                    <span class="ident">arg_value</span>(<span class="string">&quot;f2&quot;</span>, <span class="string">r#&quot;(vec![1,2], &quot;s&quot;)&quot;#</span>).<span class="ident">into</span>(),
+                ]
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert!</span>(<span class="op">!</span><span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">item_fn</span>).<span class="ident">contains</span>(<span class="string">&quot;default&quot;</span>));
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">info</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">find_default_return_type</span>() {
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                #[simple]
+                #[first(comp)]
+                #[second::default]
+                #[default(impl Iterator&lt;Item=(u32, i32)&gt;)]
+                #[last::more]
+                fn my_fix&lt;I, J&gt;(f1: I, f2: J) -&gt; impl Iterator&lt;Item=(I, J)&gt; {}
+            &quot;#</span>
+            .<span class="ident">ast</span>();
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+            <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">extract_default_type</span>(),
+                <span class="prelude-val">Some</span>(<span class="macro">parse_quote!</span> { <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">u32</span>, <span class="ident">i32</span>)<span class="op">&gt;</span> })
+            );
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">attrs</span>(<span class="string">&quot;#[simple]#[first(comp)]#[second::default]#[last::more]&quot;</span>),
+                <span class="ident">item_fn</span>.<span class="ident">attrs</span>
+            );
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">find_partials_return_type</span>() {
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                #[simple]
+                #[first(comp)]
+                #[second::default]
+                #[partial_1(impl Iterator&lt;Item=(u32, J, K)&gt;)]
+                #[partial_2(impl Iterator&lt;Item=(u32, i32, K)&gt;)]
+                #[last::more]
+                fn my_fix&lt;I, J, K&gt;(f1: I, f2: J, f3: K) -&gt; impl Iterator&lt;Item=(I, J, K)&gt; {}
+            &quot;#</span>
+            .<span class="ident">ast</span>();
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+            <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">extract_partial_type</span>(<span class="number">1</span>),
+                <span class="prelude-val">Some</span>(<span class="macro">parse_quote!</span> { <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">u32</span>, <span class="ident">J</span>, <span class="ident">K</span>)<span class="op">&gt;</span> })
+            );
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">extract_partial_type</span>(<span class="number">2</span>),
+                <span class="prelude-val">Some</span>(<span class="macro">parse_quote!</span> { <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span><span class="op">=</span>(<span class="ident">u32</span>, <span class="ident">i32</span>, <span class="ident">K</span>)<span class="op">&gt;</span> })
+            );
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">attrs</span>(<span class="string">&quot;#[simple]#[first(comp)]#[second::default]#[last::more]&quot;</span>),
+                <span class="ident">item_fn</span>.<span class="ident">attrs</span>
+            );
+        }
+
+        <span class="kw">mod</span> <span class="ident">raise_error</span> {
+            <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+            <span class="kw">use</span> <span class="ident">rstest_test::assert_in</span>;
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">for_invalid_expressions</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                fn my_fix(#[with(valid)] f1: &amp;str, #[with(with(,.,))] f2: u32, #[with(with(use))] f3: u32) {}
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>()
+                    .<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>)
+                    .<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">for_invalid_default_type</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[default(no&lt;valid::&gt;type)]
+                    fn my_fix&lt;I&gt;() -&gt; I {}
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>()
+                    .<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>)
+                    .<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">with_used_more_than_once</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    fn my_fix(#[with(1)] #[with(2)] fixture1: &amp;str, #[with(1)] #[with(2)] #[with(3)] fixture2: &amp;str) {}
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>()
+                    .<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>)
+                    .<span class="ident">err</span>()
+                    .<span class="ident">unwrap_or_default</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">3</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">from_used_more_than_once</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    fn my_fix(#[from(a)] #[from(b)] fixture1: &amp;str, #[from(c)] #[from(d)] #[from(e)] fixture2: &amp;str) {}
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>()
+                    .<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>)
+                    .<span class="ident">err</span>()
+                    .<span class="ident">unwrap_or_default</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">3</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">if_default_is_defined_more_than_once</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[default(u32)]
+                    #[default(u32)]
+                    fn my_fix&lt;I&gt;() -&gt; I {}
+                    &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+                <span class="kw">let</span> <span class="ident">error</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_in!</span>(
+                    <span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">error</span>).<span class="ident">to_lowercase</span>(),
+                    <span class="string">&quot;cannot use default more than once&quot;</span>
+                );
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">for_invalid_partial_type</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[partial_1(no&lt;valid::&gt;type)]
+                    fn my_fix&lt;I&gt;(x: I, y: u32) -&gt; I {}
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>()
+                    .<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>)
+                    .<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">if_partial_is_not_correct</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[partial_not_a_number(u32)]
+                    fn my_fix&lt;I, J&gt;(f1: I, f2: &amp;str) -&gt; I {}
+                    &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">FixtureInfo::default</span>();
+
+                <span class="kw">let</span> <span class="ident">error</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_in!</span>(
+                    <span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">error</span>).<span class="ident">to_lowercase</span>(),
+                    <span class="string">&quot;invalid partial syntax&quot;</span>
+                );
+            }
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/macros.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/macros.rs.html
new file mode 100644
index 0000000..3f8833b3
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/macros.rs.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/macros.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>macros.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1"> 1</span>
+<span id="2"> 2</span>
+<span id="3"> 3</span>
+<span id="4"> 4</span>
+<span id="5"> 5</span>
+<span id="6"> 6</span>
+<span id="7"> 7</span>
+<span id="8"> 8</span>
+<span id="9"> 9</span>
+<span id="10">10</span>
+<span id="11">11</span>
+<span id="12">12</span>
+<span id="13">13</span>
+<span id="14">14</span>
+<span id="15">15</span>
+<span id="16">16</span>
+<span id="17">17</span>
+<span id="18">18</span>
+<span id="19">19</span>
+<span id="20">20</span>
+</pre><pre class="rust">
+<span class="macro">macro_rules!</span> <span class="ident">wrap_attributes</span> {
+    (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">ident</span>:<span class="ident">ident</span>) <span class="op">=</span><span class="op">&gt;</span> {
+        <span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>, <span class="ident">Debug</span>, <span class="ident">PartialEq</span>, <span class="ident">Clone</span>)]</span>
+        <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="macro-nonterminal">$</span><span class="macro-nonterminal">ident</span> {
+            <span class="ident">inner</span>: <span class="ident">Attributes</span>,
+        }
+
+        <span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">Attributes</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="macro-nonterminal">$</span><span class="macro-nonterminal">ident</span> {
+            <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">inner</span>: <span class="ident">Attributes</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+                <span class="macro-nonterminal">$</span><span class="macro-nonterminal">ident</span> { <span class="ident">inner</span> }
+            }
+        }
+
+        <span class="kw">impl</span> <span class="macro-nonterminal">$</span><span class="macro-nonterminal">ident</span> {
+            <span class="kw">fn</span> <span class="ident">iter</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">Attribute</span><span class="op">&gt;</span> {
+                <span class="self">self</span>.<span class="ident">inner</span>.<span class="ident">attributes</span>.<span class="ident">iter</span>()
+            }
+        }
+    };
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/mod.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/mod.rs.html
new file mode 100644
index 0000000..de144fb24
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/mod.rs.html
@@ -0,0 +1,1183 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/mod.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>mod.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+<span id="370">370</span>
+<span id="371">371</span>
+<span id="372">372</span>
+<span id="373">373</span>
+<span id="374">374</span>
+<span id="375">375</span>
+<span id="376">376</span>
+<span id="377">377</span>
+<span id="378">378</span>
+<span id="379">379</span>
+<span id="380">380</span>
+<span id="381">381</span>
+<span id="382">382</span>
+<span id="383">383</span>
+<span id="384">384</span>
+<span id="385">385</span>
+<span id="386">386</span>
+<span id="387">387</span>
+<span id="388">388</span>
+<span id="389">389</span>
+<span id="390">390</span>
+<span id="391">391</span>
+<span id="392">392</span>
+<span id="393">393</span>
+<span id="394">394</span>
+<span id="395">395</span>
+<span id="396">396</span>
+<span id="397">397</span>
+<span id="398">398</span>
+<span id="399">399</span>
+<span id="400">400</span>
+<span id="401">401</span>
+<span id="402">402</span>
+<span id="403">403</span>
+<span id="404">404</span>
+<span id="405">405</span>
+<span id="406">406</span>
+<span id="407">407</span>
+<span id="408">408</span>
+<span id="409">409</span>
+<span id="410">410</span>
+<span id="411">411</span>
+<span id="412">412</span>
+<span id="413">413</span>
+<span id="414">414</span>
+<span id="415">415</span>
+<span id="416">416</span>
+<span id="417">417</span>
+<span id="418">418</span>
+<span id="419">419</span>
+<span id="420">420</span>
+<span id="421">421</span>
+<span id="422">422</span>
+<span id="423">423</span>
+<span id="424">424</span>
+<span id="425">425</span>
+<span id="426">426</span>
+<span id="427">427</span>
+<span id="428">428</span>
+<span id="429">429</span>
+<span id="430">430</span>
+<span id="431">431</span>
+<span id="432">432</span>
+<span id="433">433</span>
+<span id="434">434</span>
+<span id="435">435</span>
+<span id="436">436</span>
+<span id="437">437</span>
+<span id="438">438</span>
+<span id="439">439</span>
+<span id="440">440</span>
+<span id="441">441</span>
+<span id="442">442</span>
+<span id="443">443</span>
+<span id="444">444</span>
+<span id="445">445</span>
+<span id="446">446</span>
+<span id="447">447</span>
+<span id="448">448</span>
+<span id="449">449</span>
+<span id="450">450</span>
+<span id="451">451</span>
+<span id="452">452</span>
+<span id="453">453</span>
+<span id="454">454</span>
+<span id="455">455</span>
+<span id="456">456</span>
+<span id="457">457</span>
+<span id="458">458</span>
+<span id="459">459</span>
+<span id="460">460</span>
+<span id="461">461</span>
+<span id="462">462</span>
+<span id="463">463</span>
+<span id="464">464</span>
+<span id="465">465</span>
+<span id="466">466</span>
+<span id="467">467</span>
+<span id="468">468</span>
+<span id="469">469</span>
+<span id="470">470</span>
+<span id="471">471</span>
+<span id="472">472</span>
+<span id="473">473</span>
+<span id="474">474</span>
+<span id="475">475</span>
+<span id="476">476</span>
+<span id="477">477</span>
+<span id="478">478</span>
+<span id="479">479</span>
+<span id="480">480</span>
+<span id="481">481</span>
+<span id="482">482</span>
+<span id="483">483</span>
+<span id="484">484</span>
+<span id="485">485</span>
+<span id="486">486</span>
+<span id="487">487</span>
+<span id="488">488</span>
+<span id="489">489</span>
+<span id="490">490</span>
+<span id="491">491</span>
+<span id="492">492</span>
+<span id="493">493</span>
+<span id="494">494</span>
+<span id="495">495</span>
+<span id="496">496</span>
+<span id="497">497</span>
+<span id="498">498</span>
+<span id="499">499</span>
+<span id="500">500</span>
+<span id="501">501</span>
+<span id="502">502</span>
+<span id="503">503</span>
+<span id="504">504</span>
+<span id="505">505</span>
+<span id="506">506</span>
+<span id="507">507</span>
+<span id="508">508</span>
+<span id="509">509</span>
+<span id="510">510</span>
+<span id="511">511</span>
+<span id="512">512</span>
+<span id="513">513</span>
+<span id="514">514</span>
+<span id="515">515</span>
+<span id="516">516</span>
+<span id="517">517</span>
+<span id="518">518</span>
+<span id="519">519</span>
+<span id="520">520</span>
+<span id="521">521</span>
+<span id="522">522</span>
+<span id="523">523</span>
+<span id="524">524</span>
+<span id="525">525</span>
+<span id="526">526</span>
+<span id="527">527</span>
+<span id="528">528</span>
+<span id="529">529</span>
+<span id="530">530</span>
+<span id="531">531</span>
+<span id="532">532</span>
+<span id="533">533</span>
+<span id="534">534</span>
+<span id="535">535</span>
+<span id="536">536</span>
+<span id="537">537</span>
+<span id="538">538</span>
+<span id="539">539</span>
+<span id="540">540</span>
+<span id="541">541</span>
+<span id="542">542</span>
+<span id="543">543</span>
+<span id="544">544</span>
+<span id="545">545</span>
+<span id="546">546</span>
+<span id="547">547</span>
+<span id="548">548</span>
+<span id="549">549</span>
+<span id="550">550</span>
+<span id="551">551</span>
+<span id="552">552</span>
+<span id="553">553</span>
+<span id="554">554</span>
+<span id="555">555</span>
+<span id="556">556</span>
+<span id="557">557</span>
+<span id="558">558</span>
+<span id="559">559</span>
+<span id="560">560</span>
+<span id="561">561</span>
+<span id="562">562</span>
+<span id="563">563</span>
+<span id="564">564</span>
+<span id="565">565</span>
+<span id="566">566</span>
+<span id="567">567</span>
+<span id="568">568</span>
+<span id="569">569</span>
+<span id="570">570</span>
+<span id="571">571</span>
+<span id="572">572</span>
+<span id="573">573</span>
+<span id="574">574</span>
+<span id="575">575</span>
+<span id="576">576</span>
+<span id="577">577</span>
+<span id="578">578</span>
+<span id="579">579</span>
+<span id="580">580</span>
+<span id="581">581</span>
+<span id="582">582</span>
+<span id="583">583</span>
+<span id="584">584</span>
+<span id="585">585</span>
+<span id="586">586</span>
+<span id="587">587</span>
+<span id="588">588</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">syn</span>::{
+    <span class="ident">parse</span>::{<span class="ident">Parse</span>, <span class="ident">ParseStream</span>},
+    <span class="ident">parse_quote</span>,
+    <span class="ident">punctuated::Punctuated</span>,
+    <span class="ident">token</span>::{<span class="self">self</span>, <span class="ident">Paren</span>},
+    <span class="ident">visit_mut::VisitMut</span>,
+    <span class="ident">FnArg</span>, <span class="ident">Ident</span>, <span class="ident">ItemFn</span>, <span class="ident">Token</span>,
+};
+
+<span class="kw">use</span> <span class="kw">crate</span>::{
+    <span class="ident">error::ErrorsVec</span>,
+    <span class="ident">refident</span>::{<span class="ident">MaybeIdent</span>, <span class="ident">RefIdent</span>},
+    <span class="ident">utils</span>::{<span class="ident">attr_is</span>, <span class="ident">attr_starts_with</span>},
+};
+<span class="kw">use</span> <span class="ident">fixture</span>::{
+    <span class="ident">ArgumentValue</span>, <span class="ident">DefaultsFunctionExtractor</span>, <span class="ident">FixtureModifiers</span>, <span class="ident">FixturesFunctionExtractor</span>,
+};
+<span class="kw">use</span> <span class="ident">quote::ToTokens</span>;
+<span class="kw">use</span> <span class="ident">testcase::TestCase</span>;
+
+<span class="kw">use</span> <span class="self">self</span>::{<span class="ident">expressions::Expressions</span>, <span class="ident">vlist::ValueList</span>};
+
+<span class="comment">// To use the macros this should be the first one module</span>
+<span class="attribute">#[<span class="ident">macro_use</span>]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">macros</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">expressions</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">fixture</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">future</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">rstest</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">testcase</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">vlist</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">trait</span> <span class="ident">ExtendWithFunctionAttrs</span> {
+    <span class="kw">fn</span> <span class="ident">extend_with_function_attrs</span>(
+        <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>,
+        <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>,
+    ) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">std::result::Result</span><span class="op">&lt;</span>(), <span class="ident">ErrorsVec</span><span class="op">&gt;</span>;
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>, <span class="ident">Debug</span>, <span class="ident">PartialEq</span>, <span class="ident">Clone</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">Attributes</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">attributes</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Attribute</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">Attributes</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">vars</span> <span class="op">=</span> <span class="ident">Punctuated</span>::<span class="op">&lt;</span><span class="ident">Attribute</span>, <span class="macro">Token!</span>[::]<span class="op">&gt;</span><span class="ident">::parse_terminated</span>(<span class="ident">input</span>)<span class="question-mark">?</span>;
+        <span class="prelude-val">Ok</span>(<span class="ident">Attributes</span> {
+            <span class="ident">attributes</span>: <span class="ident">vars</span>.<span class="ident">into_iter</span>().<span class="ident">collect</span>(),
+        })
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Debug</span>, <span class="ident">PartialEq</span>, <span class="ident">Clone</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">enum</span> <span class="ident">Attribute</span> {
+    <span class="ident">Attr</span>(<span class="ident">Ident</span>),
+    <span class="ident">Tagged</span>(<span class="ident">Ident</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>),
+    <span class="ident">Type</span>(<span class="ident">Ident</span>, <span class="ident">syn::Type</span>),
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">Attribute</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek2</span>(<span class="macro">Token!</span>[<span class="op">&lt;</span>]) {
+            <span class="kw">let</span> <span class="ident">tag</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+            <span class="kw">let</span> <span class="ident">_open</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="macro">Token!</span>[<span class="op">&lt;</span>]<span class="op">&gt;</span>()<span class="question-mark">?</span>;
+            <span class="kw">let</span> <span class="ident">inner</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+            <span class="kw">let</span> <span class="ident">_close</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="macro">Token!</span>[<span class="op">&gt;</span>]<span class="op">&gt;</span>()<span class="question-mark">?</span>;
+            <span class="prelude-val">Ok</span>(<span class="ident">Attribute::Type</span>(<span class="ident">tag</span>, <span class="ident">inner</span>))
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek2</span>(<span class="macro">Token!</span>[::]) {
+            <span class="kw">let</span> <span class="ident">inner</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+            <span class="prelude-val">Ok</span>(<span class="ident">Attribute::Attr</span>(<span class="ident">inner</span>))
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek2</span>(<span class="ident">token::Paren</span>) {
+            <span class="kw">let</span> <span class="ident">tag</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+            <span class="kw">let</span> <span class="ident">content</span>;
+            <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="macro">syn::parenthesized!</span>(<span class="ident">content</span> <span class="kw">in</span> <span class="ident">input</span>);
+            <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">Punctuated</span>::<span class="op">&lt;</span><span class="ident">Ident</span>, <span class="macro">Token!</span>[,]<span class="op">&gt;</span><span class="ident">::parse_terminated</span>(<span class="kw-2">&amp;</span><span class="ident">content</span>)<span class="question-mark">?</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">collect</span>();
+
+            <span class="prelude-val">Ok</span>(<span class="ident">Attribute::Tagged</span>(<span class="ident">tag</span>, <span class="ident">args</span>))
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="ident">Attribute::Attr</span>(<span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>))
+        }
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">parse_vector_trailing_till_double_comma</span><span class="op">&lt;</span><span class="ident">T</span>, <span class="ident">P</span><span class="op">&gt;</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">T</span><span class="op">&gt;</span><span class="op">&gt;</span>
+<span class="kw">where</span>
+    <span class="ident">T</span>: <span class="ident">Parse</span>,
+    <span class="ident">P</span>: <span class="ident">syn::token::Token</span> <span class="op">+</span> <span class="ident">Parse</span>,
+{
+    <span class="prelude-val">Ok</span>(
+        <span class="ident">Punctuated</span>::<span class="op">&lt;</span><span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">T</span><span class="op">&gt;</span>, <span class="ident">P</span><span class="op">&gt;</span><span class="ident">::parse_separated_nonempty_with</span>(<span class="ident">input</span>, <span class="op">|</span><span class="ident">input_tokens</span><span class="op">|</span> {
+            <span class="kw">if</span> <span class="ident">input_tokens</span>.<span class="ident">is_empty</span>() <span class="op">|</span><span class="op">|</span> <span class="ident">input_tokens</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[::]) {
+                <span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>)
+            } <span class="kw">else</span> {
+                <span class="ident">T::parse</span>(<span class="ident">input_tokens</span>).<span class="ident">map</span>(<span class="op">|</span><span class="ident">inner</span><span class="op">|</span> <span class="prelude-val">Some</span>(<span class="ident">inner</span>))
+            }
+        })<span class="question-mark">?</span>
+        .<span class="ident">into_iter</span>()
+        .<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="ident">it</span>)
+        .<span class="ident">collect</span>(),
+    )
+}
+
+<span class="attribute">#[<span class="ident">allow</span>(<span class="ident">dead_code</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">drain_stream</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) {
+    <span class="comment">// JUST TO SKIP ALL</span>
+    <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">step</span>(<span class="op">|</span><span class="ident">cursor</span><span class="op">|</span> {
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">rest</span> <span class="op">=</span> <span class="kw-2">*</span><span class="ident">cursor</span>;
+        <span class="kw">while</span> <span class="kw">let</span> <span class="prelude-val">Some</span>((<span class="kw">_</span>, <span class="ident">next</span>)) <span class="op">=</span> <span class="ident">rest</span>.<span class="ident">token_tree</span>() {
+            <span class="ident">rest</span> <span class="op">=</span> <span class="ident">next</span>
+        }
+        <span class="prelude-val">Ok</span>(((), <span class="ident">rest</span>))
+    });
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Clone</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">Positional</span>(<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Expr</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">Positional</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Ok</span>(<span class="self">Self</span>(
+            <span class="ident">Punctuated</span>::<span class="op">&lt;</span><span class="ident">syn::Expr</span>, <span class="macro">Token!</span>[,]<span class="op">&gt;</span><span class="ident">::parse_terminated</span>(<span class="ident">input</span>)<span class="question-mark">?</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">collect</span>(),
+        ))
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Clone</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">Fixture</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">name</span>: <span class="ident">Ident</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">resolve</span>: <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">positional</span>: <span class="ident">Positional</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Fixture</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">new</span>(<span class="ident">name</span>: <span class="ident">Ident</span>, <span class="ident">resolve</span>: <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">positional</span>: <span class="ident">Positional</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span> {
+            <span class="ident">name</span>,
+            <span class="ident">resolve</span>,
+            <span class="ident">positional</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">Fixture</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">resolve</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="ident">Paren</span>) <span class="op">|</span><span class="op">|</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[<span class="kw">as</span>]) {
+            <span class="kw">let</span> <span class="ident">positional</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="ident">Paren</span>) {
+                <span class="kw">let</span> <span class="ident">content</span>;
+                <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="macro">syn::parenthesized!</span>(<span class="ident">content</span> <span class="kw">in</span> <span class="ident">input</span>);
+                <span class="ident">content</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>
+            } <span class="kw">else</span> {
+                <span class="ident">Default::default</span>()
+            };
+
+            <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[<span class="kw">as</span>]) {
+                <span class="kw">let</span> <span class="kw">_</span>: <span class="macro">Token!</span>[<span class="kw">as</span>] <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+                <span class="prelude-val">Ok</span>(<span class="self">Self</span><span class="ident">::new</span>(<span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>, <span class="prelude-val">Some</span>(<span class="ident">resolve</span>), <span class="ident">positional</span>))
+            } <span class="kw">else</span> {
+                <span class="prelude-val">Ok</span>(<span class="self">Self</span><span class="ident">::new</span>(<span class="ident">resolve</span>, <span class="prelude-val">None</span>, <span class="ident">positional</span>))
+            }
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">syn::Error::new</span>(
+                <span class="ident">input</span>.<span class="ident">span</span>(),
+                <span class="string">&quot;fixture need arguments or &#39;as new_name&#39; format&quot;</span>,
+            ))
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">RefIdent</span> <span class="kw">for</span> <span class="ident">Fixture</span> {
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span> {
+        <span class="kw-2">&amp;</span><span class="self">self</span>.<span class="ident">name</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ToTokens</span> <span class="kw">for</span> <span class="ident">Fixture</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="self">self</span>.<span class="ident">name</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>)
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_fixtures</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">fixtures_extractor</span> <span class="op">=</span> <span class="ident">FixturesFunctionExtractor::default</span>();
+    <span class="ident">fixtures_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+
+    <span class="kw">if</span> <span class="ident">fixtures_extractor</span>.<span class="number">1</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="prelude-val">Err</span>(<span class="ident">fixtures_extractor</span>.<span class="number">1</span>.<span class="ident">into</span>())
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">fixtures_extractor</span>.<span class="number">0</span>)
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_defaults</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">ArgumentValue</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">defaults_extractor</span> <span class="op">=</span> <span class="ident">DefaultsFunctionExtractor::default</span>();
+    <span class="ident">defaults_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+
+    <span class="kw">if</span> <span class="ident">defaults_extractor</span>.<span class="number">1</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="prelude-val">Err</span>(<span class="ident">defaults_extractor</span>.<span class="number">1</span>.<span class="ident">into</span>())
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">defaults_extractor</span>.<span class="number">0</span>)
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_default_return_type</span>(
+    <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::Type</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">default_type_extractor</span> <span class="op">=</span> <span class="ident">DefaultTypeFunctionExtractor::default</span>();
+    <span class="ident">default_type_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+    <span class="ident">default_type_extractor</span>.<span class="ident">take</span>()
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_partials_return_type</span>(
+    <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span>(<span class="ident">usize</span>, <span class="ident">syn::Type</span>)<span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">partials_type_extractor</span> <span class="op">=</span> <span class="ident">PartialsTypeFunctionExtractor::default</span>();
+    <span class="ident">partials_type_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+    <span class="ident">partials_type_extractor</span>.<span class="ident">take</span>()
+}
+
+<span class="kw">fn</span> <span class="ident">extract_argument_attrs</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span>, <span class="ident">B</span>: <span class="lifetime">&#39;a</span> <span class="op">+</span> <span class="ident">std::fmt::Debug</span><span class="op">&gt;</span>(
+    <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>,
+    <span class="ident">is_valid_attr</span>: <span class="kw">fn</span>(<span class="kw-2">&amp;</span><span class="ident">syn::Attribute</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span>,
+    <span class="ident">build</span>: <span class="kw">fn</span>(<span class="ident">syn::Attribute</span>, <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="ident">B</span><span class="op">&gt;</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Box</span><span class="op">&lt;</span><span class="kw">dyn</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="ident">B</span><span class="op">&gt;</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="ident">node</span>.<span class="ident">maybe_ident</span>().<span class="ident">cloned</span>();
+    <span class="kw">if</span> <span class="ident">name</span>.<span class="ident">is_none</span>() {
+        <span class="kw">return</span> <span class="ident">Box::new</span>(<span class="ident">std::iter::empty</span>());
+    }
+
+    <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="ident">name</span>.<span class="ident">unwrap</span>();
+    <span class="kw">if</span> <span class="kw">let</span> <span class="ident">FnArg::Typed</span>(<span class="kw-2">ref</span> <span class="kw-2">mut</span> <span class="ident">arg</span>) <span class="op">=</span> <span class="ident">node</span> {
+        <span class="comment">// Extract interesting attributes</span>
+        <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">arg</span>.<span class="ident">attrs</span>);
+        <span class="kw">let</span> (<span class="ident">extracted</span>, <span class="ident">remain</span>): (<span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>) <span class="op">=</span>
+            <span class="ident">attrs</span>.<span class="ident">into_iter</span>().<span class="ident">partition</span>(<span class="op">|</span><span class="ident">attr</span><span class="op">|</span> <span class="ident">is_valid_attr</span>(<span class="ident">attr</span>));
+
+        <span class="ident">arg</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="ident">remain</span>;
+
+        <span class="comment">// Parse attrs</span>
+        <span class="ident">Box::new</span>(<span class="ident">extracted</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="kw">move</span> <span class="op">|</span><span class="ident">attr</span><span class="op">|</span> <span class="ident">build</span>(<span class="ident">attr</span>, <span class="kw-2">&amp;</span><span class="ident">name</span>)))
+    } <span class="kw">else</span> {
+        <span class="ident">Box::new</span>(<span class="ident">std::iter::empty</span>())
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract default return</span>
+<span class="doccomment">/// type</span>
+<span class="kw">struct</span> <span class="ident">DefaultTypeFunctionExtractor</span>(<span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::Type</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">DefaultTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">take</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::Type</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Default</span> <span class="kw">for</span> <span class="ident">DefaultTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">default</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span>(<span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>))
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">DefaultTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_item_fn_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) {
+        <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">node</span>.<span class="ident">attrs</span>);
+        <span class="kw">let</span> (<span class="ident">defaults</span>, <span class="ident">remain</span>): (<span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>) <span class="op">=</span> <span class="ident">attrs</span>
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">partition</span>(<span class="op">|</span><span class="ident">attr</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="kw-2">&amp;</span><span class="ident">attr</span>, <span class="ident">FixtureModifiers::DEFAULT_RET_ATTR</span>));
+
+        <span class="ident">node</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="ident">remain</span>;
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">defaults</span> <span class="op">=</span> <span class="ident">defaults</span>.<span class="ident">into_iter</span>();
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">data</span> <span class="op">=</span> <span class="prelude-val">None</span>;
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">ErrorsVec::default</span>();
+        <span class="kw">match</span> <span class="ident">defaults</span>.<span class="ident">nth</span>(<span class="number">0</span>).<span class="ident">map</span>(<span class="op">|</span><span class="ident">def</span><span class="op">|</span> <span class="ident">def</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">syn::Type</span><span class="op">&gt;</span>()) {
+            <span class="prelude-val">Some</span>(<span class="prelude-val">Ok</span>(<span class="ident">t</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">data</span> <span class="op">=</span> <span class="prelude-val">Some</span>(<span class="ident">t</span>),
+            <span class="prelude-val">Some</span>(<span class="prelude-val">Err</span>(<span class="ident">e</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">errors</span>.<span class="ident">push</span>(<span class="ident">e</span>),
+            <span class="prelude-val">None</span> <span class="op">=</span><span class="op">&gt;</span> {}
+        };
+        <span class="ident">errors</span>.<span class="ident">extend</span>(
+            <span class="ident">defaults</span>.<span class="ident">map</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">syn::Error::new_spanned</span>(<span class="ident">a</span>, <span class="string">&quot;You cannot use default more than once&quot;</span>)),
+        );
+        <span class="self">self</span>.<span class="number">0</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">errors</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">errors</span>)
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="ident">data</span>)
+        };
+
+        <span class="ident">syn::visit_mut::visit_item_fn_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract default return</span>
+<span class="doccomment">/// type</span>
+<span class="kw">struct</span> <span class="ident">PartialsTypeFunctionExtractor</span>(<span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span>(<span class="ident">usize</span>, <span class="ident">syn::Type</span>)<span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">PartialsTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">take</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span>(<span class="ident">usize</span>, <span class="ident">syn::Type</span>)<span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Default</span> <span class="kw">for</span> <span class="ident">PartialsTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">default</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span>(<span class="prelude-val">Ok</span>(<span class="ident">Vec::default</span>()))
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">PartialsTypeFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_item_fn_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) {
+        <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">node</span>.<span class="ident">attrs</span>);
+        <span class="kw">let</span> (<span class="ident">partials</span>, <span class="ident">remain</span>): (<span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>) <span class="op">=</span>
+            <span class="ident">attrs</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">partition</span>(<span class="op">|</span><span class="ident">attr</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">get_ident</span>() {
+                    <span class="prelude-val">Some</span>(<span class="ident">name</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">name</span>
+                        .<span class="ident">to_string</span>()
+                        .<span class="ident">starts_with</span>(<span class="ident">FixtureModifiers::PARTIAL_RET_ATTR</span>),
+                    <span class="prelude-val">None</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+                });
+
+        <span class="ident">node</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="ident">remain</span>;
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">ErrorsVec::default</span>();
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">data</span>: <span class="ident">Vec</span><span class="op">&lt;</span>(<span class="ident">usize</span>, <span class="ident">syn::Type</span>)<span class="op">&gt;</span> <span class="op">=</span> <span class="ident">Vec::default</span>();
+        <span class="kw">for</span> <span class="ident">attr</span> <span class="kw">in</span> <span class="ident">partials</span> {
+            <span class="kw">match</span> <span class="ident">attr</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">syn::Type</span><span class="op">&gt;</span>() {
+                <span class="prelude-val">Ok</span>(<span class="ident">t</span>) <span class="op">=</span><span class="op">&gt;</span> {
+                    <span class="kw">match</span> <span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">get_ident</span>().<span class="ident">unwrap</span>().<span class="ident">to_string</span>()
+                        [<span class="ident">FixtureModifiers::PARTIAL_RET_ATTR</span>.<span class="ident">len</span>()..]
+                        .<span class="ident">parse</span>()
+                    {
+                        <span class="prelude-val">Ok</span>(<span class="ident">id</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">data</span>.<span class="ident">push</span>((<span class="ident">id</span>, <span class="ident">t</span>)),
+                        <span class="prelude-val">Err</span>(<span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">errors</span>.<span class="ident">push</span>(<span class="ident">syn::Error::new_spanned</span>(
+                            <span class="ident">attr</span>,
+                            <span class="string">&quot;Invalid partial syntax: should be partial_&lt;n_arguments&gt;&quot;</span>,
+                        )),
+                    }
+                }
+                <span class="prelude-val">Err</span>(<span class="ident">e</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">errors</span>.<span class="ident">push</span>(<span class="ident">e</span>),
+            }
+        }
+        <span class="self">self</span>.<span class="number">0</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">errors</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">errors</span>)
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="ident">data</span>)
+        };
+
+        <span class="ident">syn::visit_mut::visit_item_fn_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract case arguments and</span>
+<span class="doccomment">/// eventualy parsing errors</span>
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">struct</span> <span class="ident">CaseArgsFunctionExtractor</span>(<span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">CaseArgsFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_fn_arg_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>) {
+        <span class="kw">for</span> <span class="ident">r</span> <span class="kw">in</span> <span class="ident">extract_argument_attrs</span>(<span class="ident">node</span>, <span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="string">&quot;case&quot;</span>), <span class="op">|</span><span class="ident">_a</span>, <span class="ident">name</span><span class="op">|</span> <span class="prelude-val">Ok</span>(<span class="ident">name</span>.<span class="ident">clone</span>())) {
+            <span class="kw">match</span> <span class="ident">r</span> {
+                <span class="prelude-val">Ok</span>(<span class="ident">value</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">0</span>.<span class="ident">push</span>(<span class="ident">value</span>),
+                <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">1</span>.<span class="ident">push</span>(<span class="ident">err</span>),
+            }
+        }
+
+        <span class="ident">syn::visit_mut::visit_fn_arg_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_case_args</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">case_args_extractor</span> <span class="op">=</span> <span class="ident">CaseArgsFunctionExtractor::default</span>();
+    <span class="ident">case_args_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+
+    <span class="kw">if</span> <span class="ident">case_args_extractor</span>.<span class="number">1</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="prelude-val">Err</span>(<span class="ident">case_args_extractor</span>.<span class="number">1</span>.<span class="ident">into</span>())
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">case_args_extractor</span>.<span class="number">0</span>)
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract cases and</span>
+<span class="doccomment">/// eventualy parsing errors</span>
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">struct</span> <span class="ident">CasesFunctionExtractor</span>(<span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">TestCase</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">CasesFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_item_fn_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) {
+        <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">node</span>.<span class="ident">attrs</span>);
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">attrs_buffer</span> <span class="op">=</span> <span class="ident">Default::default</span>();
+        <span class="kw">let</span> <span class="ident">case</span>: <span class="ident">syn::PathSegment</span> <span class="op">=</span> <span class="macro">parse_quote!</span> { <span class="ident">case</span> };
+        <span class="kw">for</span> <span class="ident">attr</span> <span class="kw">in</span> <span class="ident">attrs</span>.<span class="ident">into_iter</span>() {
+            <span class="kw">if</span> <span class="ident">attr_starts_with</span>(<span class="kw-2">&amp;</span><span class="ident">attr</span>, <span class="kw-2">&amp;</span><span class="ident">case</span>) {
+                <span class="kw">match</span> <span class="ident">attr</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">Expressions</span><span class="op">&gt;</span>() {
+                    <span class="prelude-val">Ok</span>(<span class="ident">expressions</span>) <span class="op">=</span><span class="op">&gt;</span> {
+                        <span class="kw">let</span> <span class="ident">description</span> <span class="op">=</span> <span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">segments</span>.<span class="ident">into_iter</span>().<span class="ident">nth</span>(<span class="number">1</span>).<span class="ident">map</span>(<span class="op">|</span><span class="ident">p</span><span class="op">|</span> <span class="ident">p</span>.<span class="ident">ident</span>);
+                        <span class="self">self</span>.<span class="number">0</span>.<span class="ident">push</span>(<span class="ident">TestCase</span> {
+                            <span class="ident">args</span>: <span class="ident">expressions</span>.<span class="ident">into</span>(),
+                            <span class="ident">attrs</span>: <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">attrs_buffer</span>),
+                            <span class="ident">description</span>,
+                        });
+                    }
+                    <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">1</span>.<span class="ident">push</span>(<span class="ident">err</span>),
+                };
+            } <span class="kw">else</span> {
+                <span class="ident">attrs_buffer</span>.<span class="ident">push</span>(<span class="ident">attr</span>)
+            }
+        }
+        <span class="ident">node</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::take</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">attrs_buffer</span>);
+        <span class="ident">syn::visit_mut::visit_item_fn_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_cases</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">TestCase</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">cases_extractor</span> <span class="op">=</span> <span class="ident">CasesFunctionExtractor::default</span>();
+    <span class="ident">cases_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+
+    <span class="kw">if</span> <span class="ident">cases_extractor</span>.<span class="number">1</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="prelude-val">Err</span>(<span class="ident">cases_extractor</span>.<span class="number">1</span>.<span class="ident">into</span>())
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">cases_extractor</span>.<span class="number">0</span>)
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function attributes and extract value list and</span>
+<span class="doccomment">/// eventualy parsing errors</span>
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">struct</span> <span class="ident">ValueListFunctionExtractor</span>(<span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">ValueList</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">syn::Error</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">ValueListFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_fn_arg_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>) {
+        <span class="kw">for</span> <span class="ident">r</span> <span class="kw">in</span> <span class="ident">extract_argument_attrs</span>(
+            <span class="ident">node</span>,
+            <span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="string">&quot;values&quot;</span>),
+            <span class="op">|</span><span class="ident">a</span>, <span class="ident">name</span><span class="op">|</span> {
+                <span class="ident">a</span>.<span class="ident">parse_args</span>::<span class="op">&lt;</span><span class="ident">Expressions</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">v</span><span class="op">|</span> <span class="ident">ValueList</span> {
+                    <span class="ident">arg</span>: <span class="ident">name</span>.<span class="ident">clone</span>(),
+                    <span class="ident">values</span>: <span class="ident">v</span>.<span class="ident">take</span>(),
+                })
+            },
+        ) {
+            <span class="kw">match</span> <span class="ident">r</span> {
+                <span class="prelude-val">Ok</span>(<span class="ident">vlist</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">0</span>.<span class="ident">push</span>(<span class="ident">vlist</span>),
+                <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">1</span>.<span class="ident">push</span>(<span class="ident">err</span>),
+            }
+        }
+
+        <span class="ident">syn::visit_mut::visit_fn_arg_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_value_list</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">ValueList</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">vlist_extractor</span> <span class="op">=</span> <span class="ident">ValueListFunctionExtractor::default</span>();
+    <span class="ident">vlist_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+
+    <span class="kw">if</span> <span class="ident">vlist_extractor</span>.<span class="number">1</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="prelude-val">Err</span>(<span class="ident">vlist_extractor</span>.<span class="number">1</span>.<span class="ident">into</span>())
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">vlist_extractor</span>.<span class="number">0</span>)
+    }
+}
+
+<span class="doccomment">/// Simple struct used to visit function args attributes to extract the</span>
+<span class="doccomment">/// excluded ones and eventualy parsing errors</span>
+<span class="kw">struct</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor</span>(<span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>);
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">inner</span>: <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span>(<span class="ident">inner</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">take</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>
+    }
+
+    <span class="kw">fn</span> <span class="ident">update_error</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="kw-2">mut</span> <span class="ident">errors</span>: <span class="ident">ErrorsVec</span>) {
+        <span class="kw">match</span> <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>.<span class="number">0</span> {
+            <span class="prelude-val">Ok</span>(<span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="number">0</span> <span class="op">=</span> <span class="prelude-val">Err</span>(<span class="ident">errors</span>),
+            <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">err</span>.<span class="ident">append</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">errors</span>),
+        }
+        .<span class="ident">into</span>()
+    }
+
+    <span class="kw">fn</span> <span class="ident">update_excluded</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">value</span>: <span class="ident">Ident</span>) {
+        <span class="self">self</span>.<span class="number">0</span>.<span class="ident">iter_mut</span>().<span class="ident">next</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">inner</span><span class="op">|</span> {
+            <span class="ident">inner</span>.<span class="ident">push</span>(<span class="ident">value</span>);
+        });
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Default</span> <span class="kw">for</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">default</span>() <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="self">Self</span>(<span class="prelude-val">Ok</span>(<span class="ident">Default::default</span>()))
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">VisitMut</span> <span class="kw">for</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor</span> {
+    <span class="kw">fn</span> <span class="ident">visit_fn_arg_mut</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">node</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">FnArg</span>) {
+        <span class="kw">for</span> <span class="ident">r</span> <span class="kw">in</span>
+            <span class="ident">extract_argument_attrs</span>(<span class="ident">node</span>, <span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="string">&quot;notrace&quot;</span>), <span class="op">|</span><span class="ident">_a</span>, <span class="ident">name</span><span class="op">|</span> <span class="prelude-val">Ok</span>(<span class="ident">name</span>.<span class="ident">clone</span>()))
+        {
+            <span class="kw">match</span> <span class="ident">r</span> {
+                <span class="prelude-val">Ok</span>(<span class="ident">value</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="ident">update_excluded</span>(<span class="ident">value</span>),
+                <span class="prelude-val">Err</span>(<span class="ident">err</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="self">self</span>.<span class="ident">update_error</span>(<span class="ident">err</span>.<span class="ident">into</span>()),
+            }
+        }
+
+        <span class="ident">syn::visit_mut::visit_fn_arg_mut</span>(<span class="self">self</span>, <span class="ident">node</span>);
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">extract_excluded_trace</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>, <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">excluded_trace_extractor</span> <span class="op">=</span> <span class="ident">ExcludedTraceAttributesFunctionExtractor::default</span>();
+    <span class="ident">excluded_trace_extractor</span>.<span class="ident">visit_item_fn_mut</span>(<span class="ident">item_fn</span>);
+    <span class="ident">excluded_trace_extractor</span>.<span class="ident">take</span>()
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::<span class="kw-2">*</span>;
+
+    <span class="kw">mod</span> <span class="ident">parse_attributes</span> {
+        <span class="kw">use</span> <span class="kw">super</span><span class="ident">::assert_eq</span>;
+        <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+        <span class="kw">fn</span> <span class="ident">parse_attributes</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">attributes</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">Attributes</span> {
+            <span class="ident">parse_meta</span>(<span class="ident">attributes</span>)
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">one_simple_ident</span>() {
+            <span class="kw">let</span> <span class="ident">attributes</span> <span class="op">=</span> <span class="ident">parse_attributes</span>(<span class="string">&quot;my_ident&quot;</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">Attributes</span> {
+                <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::attr</span>(<span class="string">&quot;my_ident&quot;</span>)],
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">attributes</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">one_simple_group</span>() {
+            <span class="kw">let</span> <span class="ident">attributes</span> <span class="op">=</span> <span class="ident">parse_attributes</span>(<span class="string">&quot;group_tag(first, second)&quot;</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">Attributes</span> {
+                <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::tagged</span>(<span class="string">&quot;group_tag&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;first&quot;</span>, <span class="string">&quot;second&quot;</span>])],
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">attributes</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">one_simple_type</span>() {
+            <span class="kw">let</span> <span class="ident">attributes</span> <span class="op">=</span> <span class="ident">parse_attributes</span>(<span class="string">&quot;type_tag&lt;(u32, T, (String, i32))&gt;&quot;</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">Attributes</span> {
+                <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::typed</span>(<span class="string">&quot;type_tag&quot;</span>, <span class="string">&quot;(u32, T, (String, i32))&quot;</span>)],
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">attributes</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">integrated</span>() {
+            <span class="kw">let</span> <span class="ident">attributes</span> <span class="op">=</span> <span class="ident">parse_attributes</span>(
+                <span class="string">r#&quot;
+            simple :: tagged(first, second) :: type_tag&lt;(u32, T, (std::string::String, i32))&gt; :: more_tagged(a,b)&quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">Attributes</span> {
+                <span class="ident">attributes</span>: <span class="macro">vec!</span>[
+                    <span class="ident">Attribute::attr</span>(<span class="string">&quot;simple&quot;</span>),
+                    <span class="ident">Attribute::tagged</span>(<span class="string">&quot;tagged&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;first&quot;</span>, <span class="string">&quot;second&quot;</span>]),
+                    <span class="ident">Attribute::typed</span>(<span class="string">&quot;type_tag&quot;</span>, <span class="string">&quot;(u32, T, (std::string::String, i32))&quot;</span>),
+                    <span class="ident">Attribute::tagged</span>(<span class="string">&quot;more_tagged&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>]),
+                ],
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">attributes</span>);
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/rstest.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/rstest.rs.html
new file mode 100644
index 0000000..b3f70e7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/rstest.rs.html
@@ -0,0 +1,1747 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/rstest.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>rstest.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+<span id="370">370</span>
+<span id="371">371</span>
+<span id="372">372</span>
+<span id="373">373</span>
+<span id="374">374</span>
+<span id="375">375</span>
+<span id="376">376</span>
+<span id="377">377</span>
+<span id="378">378</span>
+<span id="379">379</span>
+<span id="380">380</span>
+<span id="381">381</span>
+<span id="382">382</span>
+<span id="383">383</span>
+<span id="384">384</span>
+<span id="385">385</span>
+<span id="386">386</span>
+<span id="387">387</span>
+<span id="388">388</span>
+<span id="389">389</span>
+<span id="390">390</span>
+<span id="391">391</span>
+<span id="392">392</span>
+<span id="393">393</span>
+<span id="394">394</span>
+<span id="395">395</span>
+<span id="396">396</span>
+<span id="397">397</span>
+<span id="398">398</span>
+<span id="399">399</span>
+<span id="400">400</span>
+<span id="401">401</span>
+<span id="402">402</span>
+<span id="403">403</span>
+<span id="404">404</span>
+<span id="405">405</span>
+<span id="406">406</span>
+<span id="407">407</span>
+<span id="408">408</span>
+<span id="409">409</span>
+<span id="410">410</span>
+<span id="411">411</span>
+<span id="412">412</span>
+<span id="413">413</span>
+<span id="414">414</span>
+<span id="415">415</span>
+<span id="416">416</span>
+<span id="417">417</span>
+<span id="418">418</span>
+<span id="419">419</span>
+<span id="420">420</span>
+<span id="421">421</span>
+<span id="422">422</span>
+<span id="423">423</span>
+<span id="424">424</span>
+<span id="425">425</span>
+<span id="426">426</span>
+<span id="427">427</span>
+<span id="428">428</span>
+<span id="429">429</span>
+<span id="430">430</span>
+<span id="431">431</span>
+<span id="432">432</span>
+<span id="433">433</span>
+<span id="434">434</span>
+<span id="435">435</span>
+<span id="436">436</span>
+<span id="437">437</span>
+<span id="438">438</span>
+<span id="439">439</span>
+<span id="440">440</span>
+<span id="441">441</span>
+<span id="442">442</span>
+<span id="443">443</span>
+<span id="444">444</span>
+<span id="445">445</span>
+<span id="446">446</span>
+<span id="447">447</span>
+<span id="448">448</span>
+<span id="449">449</span>
+<span id="450">450</span>
+<span id="451">451</span>
+<span id="452">452</span>
+<span id="453">453</span>
+<span id="454">454</span>
+<span id="455">455</span>
+<span id="456">456</span>
+<span id="457">457</span>
+<span id="458">458</span>
+<span id="459">459</span>
+<span id="460">460</span>
+<span id="461">461</span>
+<span id="462">462</span>
+<span id="463">463</span>
+<span id="464">464</span>
+<span id="465">465</span>
+<span id="466">466</span>
+<span id="467">467</span>
+<span id="468">468</span>
+<span id="469">469</span>
+<span id="470">470</span>
+<span id="471">471</span>
+<span id="472">472</span>
+<span id="473">473</span>
+<span id="474">474</span>
+<span id="475">475</span>
+<span id="476">476</span>
+<span id="477">477</span>
+<span id="478">478</span>
+<span id="479">479</span>
+<span id="480">480</span>
+<span id="481">481</span>
+<span id="482">482</span>
+<span id="483">483</span>
+<span id="484">484</span>
+<span id="485">485</span>
+<span id="486">486</span>
+<span id="487">487</span>
+<span id="488">488</span>
+<span id="489">489</span>
+<span id="490">490</span>
+<span id="491">491</span>
+<span id="492">492</span>
+<span id="493">493</span>
+<span id="494">494</span>
+<span id="495">495</span>
+<span id="496">496</span>
+<span id="497">497</span>
+<span id="498">498</span>
+<span id="499">499</span>
+<span id="500">500</span>
+<span id="501">501</span>
+<span id="502">502</span>
+<span id="503">503</span>
+<span id="504">504</span>
+<span id="505">505</span>
+<span id="506">506</span>
+<span id="507">507</span>
+<span id="508">508</span>
+<span id="509">509</span>
+<span id="510">510</span>
+<span id="511">511</span>
+<span id="512">512</span>
+<span id="513">513</span>
+<span id="514">514</span>
+<span id="515">515</span>
+<span id="516">516</span>
+<span id="517">517</span>
+<span id="518">518</span>
+<span id="519">519</span>
+<span id="520">520</span>
+<span id="521">521</span>
+<span id="522">522</span>
+<span id="523">523</span>
+<span id="524">524</span>
+<span id="525">525</span>
+<span id="526">526</span>
+<span id="527">527</span>
+<span id="528">528</span>
+<span id="529">529</span>
+<span id="530">530</span>
+<span id="531">531</span>
+<span id="532">532</span>
+<span id="533">533</span>
+<span id="534">534</span>
+<span id="535">535</span>
+<span id="536">536</span>
+<span id="537">537</span>
+<span id="538">538</span>
+<span id="539">539</span>
+<span id="540">540</span>
+<span id="541">541</span>
+<span id="542">542</span>
+<span id="543">543</span>
+<span id="544">544</span>
+<span id="545">545</span>
+<span id="546">546</span>
+<span id="547">547</span>
+<span id="548">548</span>
+<span id="549">549</span>
+<span id="550">550</span>
+<span id="551">551</span>
+<span id="552">552</span>
+<span id="553">553</span>
+<span id="554">554</span>
+<span id="555">555</span>
+<span id="556">556</span>
+<span id="557">557</span>
+<span id="558">558</span>
+<span id="559">559</span>
+<span id="560">560</span>
+<span id="561">561</span>
+<span id="562">562</span>
+<span id="563">563</span>
+<span id="564">564</span>
+<span id="565">565</span>
+<span id="566">566</span>
+<span id="567">567</span>
+<span id="568">568</span>
+<span id="569">569</span>
+<span id="570">570</span>
+<span id="571">571</span>
+<span id="572">572</span>
+<span id="573">573</span>
+<span id="574">574</span>
+<span id="575">575</span>
+<span id="576">576</span>
+<span id="577">577</span>
+<span id="578">578</span>
+<span id="579">579</span>
+<span id="580">580</span>
+<span id="581">581</span>
+<span id="582">582</span>
+<span id="583">583</span>
+<span id="584">584</span>
+<span id="585">585</span>
+<span id="586">586</span>
+<span id="587">587</span>
+<span id="588">588</span>
+<span id="589">589</span>
+<span id="590">590</span>
+<span id="591">591</span>
+<span id="592">592</span>
+<span id="593">593</span>
+<span id="594">594</span>
+<span id="595">595</span>
+<span id="596">596</span>
+<span id="597">597</span>
+<span id="598">598</span>
+<span id="599">599</span>
+<span id="600">600</span>
+<span id="601">601</span>
+<span id="602">602</span>
+<span id="603">603</span>
+<span id="604">604</span>
+<span id="605">605</span>
+<span id="606">606</span>
+<span id="607">607</span>
+<span id="608">608</span>
+<span id="609">609</span>
+<span id="610">610</span>
+<span id="611">611</span>
+<span id="612">612</span>
+<span id="613">613</span>
+<span id="614">614</span>
+<span id="615">615</span>
+<span id="616">616</span>
+<span id="617">617</span>
+<span id="618">618</span>
+<span id="619">619</span>
+<span id="620">620</span>
+<span id="621">621</span>
+<span id="622">622</span>
+<span id="623">623</span>
+<span id="624">624</span>
+<span id="625">625</span>
+<span id="626">626</span>
+<span id="627">627</span>
+<span id="628">628</span>
+<span id="629">629</span>
+<span id="630">630</span>
+<span id="631">631</span>
+<span id="632">632</span>
+<span id="633">633</span>
+<span id="634">634</span>
+<span id="635">635</span>
+<span id="636">636</span>
+<span id="637">637</span>
+<span id="638">638</span>
+<span id="639">639</span>
+<span id="640">640</span>
+<span id="641">641</span>
+<span id="642">642</span>
+<span id="643">643</span>
+<span id="644">644</span>
+<span id="645">645</span>
+<span id="646">646</span>
+<span id="647">647</span>
+<span id="648">648</span>
+<span id="649">649</span>
+<span id="650">650</span>
+<span id="651">651</span>
+<span id="652">652</span>
+<span id="653">653</span>
+<span id="654">654</span>
+<span id="655">655</span>
+<span id="656">656</span>
+<span id="657">657</span>
+<span id="658">658</span>
+<span id="659">659</span>
+<span id="660">660</span>
+<span id="661">661</span>
+<span id="662">662</span>
+<span id="663">663</span>
+<span id="664">664</span>
+<span id="665">665</span>
+<span id="666">666</span>
+<span id="667">667</span>
+<span id="668">668</span>
+<span id="669">669</span>
+<span id="670">670</span>
+<span id="671">671</span>
+<span id="672">672</span>
+<span id="673">673</span>
+<span id="674">674</span>
+<span id="675">675</span>
+<span id="676">676</span>
+<span id="677">677</span>
+<span id="678">678</span>
+<span id="679">679</span>
+<span id="680">680</span>
+<span id="681">681</span>
+<span id="682">682</span>
+<span id="683">683</span>
+<span id="684">684</span>
+<span id="685">685</span>
+<span id="686">686</span>
+<span id="687">687</span>
+<span id="688">688</span>
+<span id="689">689</span>
+<span id="690">690</span>
+<span id="691">691</span>
+<span id="692">692</span>
+<span id="693">693</span>
+<span id="694">694</span>
+<span id="695">695</span>
+<span id="696">696</span>
+<span id="697">697</span>
+<span id="698">698</span>
+<span id="699">699</span>
+<span id="700">700</span>
+<span id="701">701</span>
+<span id="702">702</span>
+<span id="703">703</span>
+<span id="704">704</span>
+<span id="705">705</span>
+<span id="706">706</span>
+<span id="707">707</span>
+<span id="708">708</span>
+<span id="709">709</span>
+<span id="710">710</span>
+<span id="711">711</span>
+<span id="712">712</span>
+<span id="713">713</span>
+<span id="714">714</span>
+<span id="715">715</span>
+<span id="716">716</span>
+<span id="717">717</span>
+<span id="718">718</span>
+<span id="719">719</span>
+<span id="720">720</span>
+<span id="721">721</span>
+<span id="722">722</span>
+<span id="723">723</span>
+<span id="724">724</span>
+<span id="725">725</span>
+<span id="726">726</span>
+<span id="727">727</span>
+<span id="728">728</span>
+<span id="729">729</span>
+<span id="730">730</span>
+<span id="731">731</span>
+<span id="732">732</span>
+<span id="733">733</span>
+<span id="734">734</span>
+<span id="735">735</span>
+<span id="736">736</span>
+<span id="737">737</span>
+<span id="738">738</span>
+<span id="739">739</span>
+<span id="740">740</span>
+<span id="741">741</span>
+<span id="742">742</span>
+<span id="743">743</span>
+<span id="744">744</span>
+<span id="745">745</span>
+<span id="746">746</span>
+<span id="747">747</span>
+<span id="748">748</span>
+<span id="749">749</span>
+<span id="750">750</span>
+<span id="751">751</span>
+<span id="752">752</span>
+<span id="753">753</span>
+<span id="754">754</span>
+<span id="755">755</span>
+<span id="756">756</span>
+<span id="757">757</span>
+<span id="758">758</span>
+<span id="759">759</span>
+<span id="760">760</span>
+<span id="761">761</span>
+<span id="762">762</span>
+<span id="763">763</span>
+<span id="764">764</span>
+<span id="765">765</span>
+<span id="766">766</span>
+<span id="767">767</span>
+<span id="768">768</span>
+<span id="769">769</span>
+<span id="770">770</span>
+<span id="771">771</span>
+<span id="772">772</span>
+<span id="773">773</span>
+<span id="774">774</span>
+<span id="775">775</span>
+<span id="776">776</span>
+<span id="777">777</span>
+<span id="778">778</span>
+<span id="779">779</span>
+<span id="780">780</span>
+<span id="781">781</span>
+<span id="782">782</span>
+<span id="783">783</span>
+<span id="784">784</span>
+<span id="785">785</span>
+<span id="786">786</span>
+<span id="787">787</span>
+<span id="788">788</span>
+<span id="789">789</span>
+<span id="790">790</span>
+<span id="791">791</span>
+<span id="792">792</span>
+<span id="793">793</span>
+<span id="794">794</span>
+<span id="795">795</span>
+<span id="796">796</span>
+<span id="797">797</span>
+<span id="798">798</span>
+<span id="799">799</span>
+<span id="800">800</span>
+<span id="801">801</span>
+<span id="802">802</span>
+<span id="803">803</span>
+<span id="804">804</span>
+<span id="805">805</span>
+<span id="806">806</span>
+<span id="807">807</span>
+<span id="808">808</span>
+<span id="809">809</span>
+<span id="810">810</span>
+<span id="811">811</span>
+<span id="812">812</span>
+<span id="813">813</span>
+<span id="814">814</span>
+<span id="815">815</span>
+<span id="816">816</span>
+<span id="817">817</span>
+<span id="818">818</span>
+<span id="819">819</span>
+<span id="820">820</span>
+<span id="821">821</span>
+<span id="822">822</span>
+<span id="823">823</span>
+<span id="824">824</span>
+<span id="825">825</span>
+<span id="826">826</span>
+<span id="827">827</span>
+<span id="828">828</span>
+<span id="829">829</span>
+<span id="830">830</span>
+<span id="831">831</span>
+<span id="832">832</span>
+<span id="833">833</span>
+<span id="834">834</span>
+<span id="835">835</span>
+<span id="836">836</span>
+<span id="837">837</span>
+<span id="838">838</span>
+<span id="839">839</span>
+<span id="840">840</span>
+<span id="841">841</span>
+<span id="842">842</span>
+<span id="843">843</span>
+<span id="844">844</span>
+<span id="845">845</span>
+<span id="846">846</span>
+<span id="847">847</span>
+<span id="848">848</span>
+<span id="849">849</span>
+<span id="850">850</span>
+<span id="851">851</span>
+<span id="852">852</span>
+<span id="853">853</span>
+<span id="854">854</span>
+<span id="855">855</span>
+<span id="856">856</span>
+<span id="857">857</span>
+<span id="858">858</span>
+<span id="859">859</span>
+<span id="860">860</span>
+<span id="861">861</span>
+<span id="862">862</span>
+<span id="863">863</span>
+<span id="864">864</span>
+<span id="865">865</span>
+<span id="866">866</span>
+<span id="867">867</span>
+<span id="868">868</span>
+<span id="869">869</span>
+<span id="870">870</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">syn</span>::{
+    <span class="ident">parse</span>::{<span class="ident">Parse</span>, <span class="ident">ParseStream</span>},
+    <span class="ident">Ident</span>, <span class="ident">ItemFn</span>, <span class="ident">Token</span>,
+};
+
+<span class="kw">use</span> <span class="kw">super</span><span class="ident">::testcase::TestCase</span>;
+<span class="kw">use</span> <span class="kw">super</span>::{
+    <span class="ident">extract_case_args</span>, <span class="ident">extract_cases</span>, <span class="ident">extract_excluded_trace</span>, <span class="ident">extract_fixtures</span>, <span class="ident">extract_value_list</span>,
+    <span class="ident">parse_vector_trailing_till_double_comma</span>, <span class="ident">Attribute</span>, <span class="ident">Attributes</span>, <span class="ident">ExtendWithFunctionAttrs</span>,
+    <span class="ident">Fixture</span>,
+};
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse::vlist::ValueList</span>;
+<span class="kw">use</span> <span class="kw">crate</span>::{
+    <span class="ident">error::ErrorsVec</span>,
+    <span class="ident">refident</span>::{<span class="ident">MaybeIdent</span>, <span class="ident">RefIdent</span>},
+};
+<span class="kw">use</span> <span class="ident">proc_macro2</span>::{<span class="ident">Span</span>, <span class="ident">TokenStream</span>};
+<span class="kw">use</span> <span class="ident">quote</span>::{<span class="ident">format_ident</span>, <span class="ident">ToTokens</span>};
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">RsTestInfo</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">data</span>: <span class="ident">RsTestData</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">attributes</span>: <span class="ident">RsTestAttributes</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">RsTestInfo</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Ok</span>(<span class="kw">if</span> <span class="ident">input</span>.<span class="ident">is_empty</span>() {
+            <span class="ident">Default::default</span>()
+        } <span class="kw">else</span> {
+            <span class="self">Self</span> {
+                <span class="ident">data</span>: <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>,
+                <span class="ident">attributes</span>: <span class="ident">input</span>
+                    .<span class="ident">parse</span>::<span class="op">&lt;</span><span class="macro">Token!</span>[::]<span class="op">&gt;</span>()
+                    .<span class="ident">or_else</span>(<span class="op">|</span><span class="kw">_</span><span class="op">|</span> <span class="prelude-val">Ok</span>(<span class="ident">Default::default</span>()))
+                    .<span class="ident">and_then</span>(<span class="op">|</span><span class="kw">_</span><span class="op">|</span> <span class="ident">input</span>.<span class="ident">parse</span>())<span class="question-mark">?</span>,
+            }
+        })
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ExtendWithFunctionAttrs</span> <span class="kw">for</span> <span class="ident">RsTestInfo</span> {
+    <span class="kw">fn</span> <span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span>(), <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> (<span class="kw">_</span>, <span class="ident">excluded</span>) <span class="op">=</span> <span class="macro">merge_errors!</span>(
+            <span class="self">self</span>.<span class="ident">data</span>.<span class="ident">extend_with_function_attrs</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_excluded_trace</span>(<span class="ident">item_fn</span>)
+        )<span class="question-mark">?</span>;
+        <span class="self">self</span>.<span class="ident">attributes</span>.<span class="ident">add_notraces</span>(<span class="ident">excluded</span>);
+        <span class="prelude-val">Ok</span>(())
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Default</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">RsTestData</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">items</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">RsTestItem</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">RsTestData</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">case_args</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">it</span> {
+            <span class="ident">RsTestItem::CaseArgName</span>(<span class="kw-2">ref</span> <span class="ident">arg</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">arg</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+
+    <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">dead_code</span>)]</span>
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">has_case_args</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="self">self</span>.<span class="ident">case_args</span>().<span class="ident">next</span>().<span class="ident">is_some</span>()
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">cases</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">TestCase</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">it</span> {
+            <span class="ident">RsTestItem::TestCase</span>(<span class="kw-2">ref</span> <span class="ident">case</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">case</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">has_cases</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="self">self</span>.<span class="ident">cases</span>().<span class="ident">next</span>().<span class="ident">is_some</span>()
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fixtures</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">Fixture</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">it</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">it</span> {
+            <span class="ident">RsTestItem::Fixture</span>(<span class="kw-2">ref</span> <span class="ident">fixture</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">fixture</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+
+    <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">dead_code</span>)]</span>
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">has_fixtures</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="self">self</span>.<span class="ident">fixtures</span>().<span class="ident">next</span>().<span class="ident">is_some</span>()
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">list_values</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">ValueList</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">iter</span>().<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">mv</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">mv</span> {
+            <span class="ident">RsTestItem::ValueList</span>(<span class="kw-2">ref</span> <span class="ident">value_list</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">value_list</span>),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        })
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">has_list_values</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="self">self</span>.<span class="ident">list_values</span>().<span class="ident">next</span>().<span class="ident">is_some</span>()
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">RsTestData</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[::]) {
+            <span class="prelude-val">Ok</span>(<span class="ident">Default::default</span>())
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="self">Self</span> {
+                <span class="ident">items</span>: <span class="ident">parse_vector_trailing_till_double_comma</span>::<span class="op">&lt;</span><span class="kw">_</span>, <span class="macro">Token!</span>[,]<span class="op">&gt;</span>(<span class="ident">input</span>)<span class="question-mark">?</span>,
+            })
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ExtendWithFunctionAttrs</span> <span class="kw">for</span> <span class="ident">RsTestData</span> {
+    <span class="kw">fn</span> <span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span>(), <span class="ident">ErrorsVec</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="macro">composed_tuple!</span>(<span class="ident">fixtures</span>, <span class="ident">case_args</span>, <span class="ident">cases</span>, <span class="ident">value_list</span>) <span class="op">=</span> <span class="macro">merge_errors!</span>(
+            <span class="ident">extract_fixtures</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_case_args</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_cases</span>(<span class="ident">item_fn</span>),
+            <span class="ident">extract_value_list</span>(<span class="ident">item_fn</span>)
+        )<span class="question-mark">?</span>;
+
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">extend</span>(<span class="ident">fixtures</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">into</span>()));
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">extend</span>(<span class="ident">case_args</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">into</span>()));
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">extend</span>(<span class="ident">cases</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">into</span>()));
+        <span class="self">self</span>.<span class="ident">items</span>.<span class="ident">extend</span>(<span class="ident">value_list</span>.<span class="ident">into_iter</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">into</span>()));
+        <span class="prelude-val">Ok</span>(())
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">enum</span> <span class="ident">RsTestItem</span> {
+    <span class="ident">Fixture</span>(<span class="ident">Fixture</span>),
+    <span class="ident">CaseArgName</span>(<span class="ident">Ident</span>),
+    <span class="ident">TestCase</span>(<span class="ident">TestCase</span>),
+    <span class="ident">ValueList</span>(<span class="ident">ValueList</span>),
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">f</span>: <span class="ident">Fixture</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">RsTestItem::Fixture</span>(<span class="ident">f</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">ident</span>: <span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">RsTestItem::CaseArgName</span>(<span class="ident">ident</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">TestCase</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">case</span>: <span class="ident">TestCase</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">RsTestItem::TestCase</span>(<span class="ident">case</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">From</span><span class="op">&lt;</span><span class="ident">ValueList</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">from</span>(<span class="ident">value_list</span>: <span class="ident">ValueList</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">RsTestItem::ValueList</span>(<span class="ident">value_list</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">fork</span>().<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">TestCase</span><span class="op">&gt;</span>().<span class="ident">is_ok</span>() {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">TestCase</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="ident">RsTestItem::TestCase</span>)
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek2</span>(<span class="macro">Token!</span>[<span class="op">=</span><span class="op">&gt;</span>]) {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">ValueList</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="ident">RsTestItem::ValueList</span>)
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">fork</span>().<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span>().<span class="ident">is_ok</span>() {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Fixture</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="ident">RsTestItem::Fixture</span>)
+        } <span class="kw">else</span> <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">fork</span>().<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>().<span class="ident">is_ok</span>() {
+            <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>().<span class="ident">map</span>(<span class="ident">RsTestItem::CaseArgName</span>)
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">syn::Error::new</span>(<span class="ident">Span::call_site</span>(), <span class="string">&quot;Cannot parse it&quot;</span>))
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="kw">use</span> <span class="ident">RsTestItem</span>::<span class="kw-2">*</span>;
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">Fixture</span>(<span class="kw-2">ref</span> <span class="ident">fixture</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">fixture</span>.<span class="ident">ident</span>()),
+            <span class="ident">CaseArgName</span>(<span class="kw-2">ref</span> <span class="ident">case_arg</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">case_arg</span>),
+            <span class="ident">ValueList</span>(<span class="kw-2">ref</span> <span class="ident">value_list</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">value_list</span>.<span class="ident">ident</span>()),
+            <span class="ident">TestCase</span>(<span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ToTokens</span> <span class="kw">for</span> <span class="ident">RsTestItem</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="kw">use</span> <span class="ident">RsTestItem</span>::<span class="kw-2">*</span>;
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">Fixture</span>(<span class="kw-2">ref</span> <span class="ident">fixture</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">fixture</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>),
+            <span class="ident">CaseArgName</span>(<span class="kw-2">ref</span> <span class="ident">case_arg</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">case_arg</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>),
+            <span class="ident">TestCase</span>(<span class="kw-2">ref</span> <span class="ident">case</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">case</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>),
+            <span class="ident">ValueList</span>(<span class="kw-2">ref</span> <span class="ident">list</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">list</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>),
+        }
+    }
+}
+
+<span class="macro">wrap_attributes!</span>(<span class="ident">RsTestAttributes</span>);
+
+<span class="kw">impl</span> <span class="ident">RsTestAttributes</span> {
+    <span class="kw">const</span> <span class="ident">TRACE_VARIABLE_ATTR</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;static</span> <span class="ident">str</span> <span class="op">=</span> <span class="string">&quot;trace&quot;</span>;
+    <span class="kw">const</span> <span class="ident">NOTRACE_VARIABLE_ATTR</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;static</span> <span class="ident">str</span> <span class="op">=</span> <span class="string">&quot;notrace&quot;</span>;
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">trace_me</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="kw">if</span> <span class="self">self</span>.<span class="ident">should_trace</span>() {
+            <span class="self">self</span>.<span class="ident">iter</span>()
+                .<span class="ident">filter</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">m</span><span class="op">|</span> <span class="self">Self</span><span class="ident">::is_notrace</span>(<span class="ident">ident</span>, <span class="ident">m</span>))
+                .<span class="ident">next</span>()
+                .<span class="ident">is_none</span>()
+        } <span class="kw">else</span> {
+            <span class="bool-val">false</span>
+        }
+    }
+
+    <span class="kw">fn</span> <span class="ident">is_notrace</span>(<span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>, <span class="ident">m</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="kw">match</span> <span class="ident">m</span> {
+            <span class="ident">Attribute::Tagged</span>(<span class="ident">i</span>, <span class="ident">args</span>) <span class="kw">if</span> <span class="ident">i</span> <span class="op">=</span><span class="op">=</span> <span class="self">Self</span><span class="ident">::NOTRACE_VARIABLE_ATTR</span> <span class="op">=</span><span class="op">&gt;</span> {
+                <span class="ident">args</span>.<span class="ident">iter</span>().<span class="ident">find</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">a</span><span class="op">|</span> <span class="ident">a</span> <span class="op">=</span><span class="op">=</span> <span class="ident">ident</span>).<span class="ident">is_some</span>()
+            }
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+        }
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">should_trace</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="self">self</span>.<span class="ident">iter</span>().<span class="ident">filter</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">m</span><span class="op">|</span> <span class="self">Self</span><span class="ident">::is_trace</span>(<span class="ident">m</span>)).<span class="ident">next</span>().<span class="ident">is_some</span>()
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">add_trace</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">trace</span>: <span class="ident">Ident</span>) {
+        <span class="self">self</span>.<span class="ident">inner</span>.<span class="ident">attributes</span>.<span class="ident">push</span>(<span class="ident">Attribute::Attr</span>(<span class="ident">trace</span>));
+    }
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">add_notraces</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">notraces</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>) {
+        <span class="kw">if</span> <span class="ident">notraces</span>.<span class="ident">is_empty</span>() {
+            <span class="kw">return</span>;
+        }
+        <span class="self">self</span>.<span class="ident">inner</span>.<span class="ident">attributes</span>.<span class="ident">push</span>(<span class="ident">Attribute::Tagged</span>(
+            <span class="macro">format_ident!</span>(<span class="string">&quot;{}&quot;</span>, <span class="self">Self</span><span class="ident">::NOTRACE_VARIABLE_ATTR</span>),
+            <span class="ident">notraces</span>,
+        ));
+    }
+
+    <span class="kw">fn</span> <span class="ident">is_trace</span>(<span class="ident">m</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="kw">match</span> <span class="ident">m</span> {
+            <span class="ident">Attribute::Attr</span>(<span class="ident">i</span>) <span class="kw">if</span> <span class="ident">i</span> <span class="op">=</span><span class="op">=</span> <span class="self">Self</span><span class="ident">::TRACE_VARIABLE_ATTR</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">true</span>,
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">RsTestAttributes</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Ok</span>(<span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="ident">Attributes</span><span class="op">&gt;</span>()<span class="question-mark">?</span>.<span class="ident">into</span>())
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">test</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::<span class="kw-2">*</span>;
+
+    <span class="kw">mod</span> <span class="ident">parse_rstest_data</span> {
+        <span class="kw">use</span> <span class="kw">super</span><span class="ident">::assert_eq</span>;
+        <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+        <span class="kw">fn</span> <span class="ident">parse_rstest_data</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">fixtures</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">RsTestData</span> {
+            <span class="ident">parse_meta</span>(<span class="ident">fixtures</span>)
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">one_arg</span>() {
+            <span class="kw">let</span> <span class="ident">fixtures</span> <span class="op">=</span> <span class="ident">parse_rstest_data</span>(<span class="string">&quot;my_fixture(42)&quot;</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestData</span> {
+                <span class="ident">items</span>: <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>]).<span class="ident">into</span>()],
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">fixtures</span>);
+        }
+    }
+
+    <span class="kw">fn</span> <span class="ident">parse_rstest</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">rstest_data</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">RsTestInfo</span> {
+        <span class="ident">parse_meta</span>(<span class="ident">rstest_data</span>)
+    }
+
+    <span class="kw">mod</span> <span class="ident">no_cases</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+        <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse</span>::{<span class="ident">Attribute</span>, <span class="ident">Attributes</span>};
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">happy_path</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;my_fixture(42, &quot;other&quot;), other(vec![42])
+            :: trace :: no_trace(some)&quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[
+                    <span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]).<span class="ident">into</span>(),
+                    <span class="ident">fixture</span>(<span class="string">&quot;other&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;vec![42]&quot;</span>]).<span class="ident">into</span>(),
+                ]
+                .<span class="ident">into</span>(),
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[
+                        <span class="ident">Attribute::attr</span>(<span class="string">&quot;trace&quot;</span>),
+                        <span class="ident">Attribute::tagged</span>(<span class="string">&quot;no_trace&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;some&quot;</span>]),
+                    ],
+                }
+                .<span class="ident">into</span>(),
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="kw">mod</span> <span class="ident">fixture_extraction</span> {
+            <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">rename</span>() {
+                <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                    <span class="string">r#&quot;long_fixture_name(42, &quot;other&quot;) as short, simple as s, no_change()&quot;#</span>,
+                );
+
+                <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                    <span class="ident">data</span>: <span class="macro">vec!</span>[
+                        <span class="ident">fixture</span>(<span class="string">&quot;short&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>])
+                            .<span class="ident">with_resolve</span>(<span class="string">&quot;long_fixture_name&quot;</span>)
+                            .<span class="ident">into</span>(),
+                        <span class="ident">fixture</span>(<span class="string">&quot;s&quot;</span>, <span class="kw-2">&amp;</span>[]).<span class="ident">with_resolve</span>(<span class="string">&quot;simple&quot;</span>).<span class="ident">into</span>(),
+                        <span class="ident">fixture</span>(<span class="string">&quot;no_change&quot;</span>, <span class="kw-2">&amp;</span>[]).<span class="ident">into</span>(),
+                    ]
+                    .<span class="ident">into</span>(),
+                    ..<span class="ident">Default::default</span>()
+                };
+
+                <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">rename_with_attributes</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    fn test_fn(
+                        #[from(long_fixture_name)] 
+                        #[with(42, &quot;other&quot;)] short: u32, 
+                        #[from(simple)]
+                        s: &amp;str,
+                        no_change: i32) {
+                    }
+                    &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                    <span class="ident">data</span>: <span class="macro">vec!</span>[
+                        <span class="ident">fixture</span>(<span class="string">&quot;short&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>])
+                            .<span class="ident">with_resolve</span>(<span class="string">&quot;long_fixture_name&quot;</span>)
+                            .<span class="ident">into</span>(),
+                        <span class="ident">fixture</span>(<span class="string">&quot;s&quot;</span>, <span class="kw-2">&amp;</span>[]).<span class="ident">with_resolve</span>(<span class="string">&quot;simple&quot;</span>).<span class="ident">into</span>()
+                    ]
+                    .<span class="ident">into</span>(),
+                    ..<span class="ident">Default::default</span>()
+                };
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">data</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+            }
+            
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">defined_via_with_attributes</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    fn test_fn(#[with(42, &quot;other&quot;)] my_fixture: u32, #[with(vec![42])] other: &amp;str) {
+                    }
+                    &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                    <span class="ident">data</span>: <span class="macro">vec!</span>[
+                        <span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]).<span class="ident">into</span>(),
+                        <span class="ident">fixture</span>(<span class="string">&quot;other&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;vec![42]&quot;</span>]).<span class="ident">into</span>(),
+                    ]
+                    .<span class="ident">into</span>(),
+                    ..<span class="ident">Default::default</span>()
+                };
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">data</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+            }
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">empty_fixtures</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(<span class="string">r#&quot;::trace::no_trace(some)&quot;#</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[
+                        <span class="ident">Attribute::attr</span>(<span class="string">&quot;trace&quot;</span>),
+                        <span class="ident">Attribute::tagged</span>(<span class="string">&quot;no_trace&quot;</span>, <span class="macro">vec!</span>[<span class="string">&quot;some&quot;</span>]),
+                    ],
+                }
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">empty_attributes</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(<span class="string">r#&quot;my_fixture(42, &quot;other&quot;)&quot;#</span>);
+
+            <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">RsTestInfo</span> {
+                <span class="ident">data</span>: <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]).<span class="ident">into</span>()].<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            };
+
+            <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">data</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">extract_notrace_args_atttribute</span>() {
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+            fn test_fn(#[notrace] a: u32, #[something_else] b: &amp;str, #[notrace] c: i32) {
+            }
+            &quot;#</span>
+            .<span class="ident">ast</span>();
+
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+            <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+            <span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">add_trace</span>(<span class="ident">ident</span>(<span class="string">&quot;trace&quot;</span>));
+
+            <span class="macro">assert!</span>(<span class="op">!</span><span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">trace_me</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;a&quot;</span>)));
+            <span class="macro">assert!</span>(<span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">trace_me</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;b&quot;</span>)));
+            <span class="macro">assert!</span>(<span class="op">!</span><span class="ident">info</span>.<span class="ident">attributes</span>.<span class="ident">trace_me</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;c&quot;</span>)));
+            <span class="kw">let</span> <span class="ident">b_args</span> <span class="op">=</span> <span class="ident">item_fn</span>
+                .<span class="ident">sig</span>
+                .<span class="ident">inputs</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">nth</span>(<span class="number">1</span>)
+                .<span class="ident">and_then</span>(<span class="op">|</span><span class="ident">id</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">id</span> {
+                    <span class="ident">syn::FnArg::Typed</span>(<span class="ident">arg</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">arg</span>.<span class="ident">attrs</span>),
+                    <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+                })
+                .<span class="ident">unwrap</span>();
+            <span class="macro">assert_eq!</span>(<span class="ident">attrs</span>(<span class="string">&quot;#[something_else]&quot;</span>), <span class="ident">b_args</span>);
+        }
+    }
+
+    <span class="kw">mod</span> <span class="ident">parametrize_cases</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+        <span class="kw">use</span> <span class="ident">std::iter::FromIterator</span>;
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">one_simple_case_one_arg</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(<span class="string">r#&quot;arg, case(42)&quot;#</span>).<span class="ident">data</span>;
+
+            <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">case_args</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">args</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">cases</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="string">&quot;arg&quot;</span>, <span class="kw-2">&amp;</span><span class="ident">args</span>[<span class="number">0</span>].<span class="ident">to_string</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>]), <span class="ident">cases</span>[<span class="number">0</span>].<span class="ident">args</span>())
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">happy_path</span>() {
+            <span class="kw">let</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                my_fixture(42,&quot;foo&quot;),
+                arg1, arg2, arg3,
+                case(1,2,3),
+                case(11,12,13),
+                case(21,22,23)
+            &quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>;
+            <span class="kw">let</span> <span class="ident">fixtures</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">fixtures</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;my_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;foo&quot;&quot;#</span>])],
+                <span class="ident">fixtures</span>
+            );
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">to_strs!</span>(<span class="macro">vec!</span>[<span class="string">&quot;arg1&quot;</span>, <span class="string">&quot;arg2&quot;</span>, <span class="string">&quot;arg3&quot;</span>]),
+                <span class="ident">data</span>.<span class="ident">case_args</span>()
+                    .<span class="ident">map</span>(<span class="ident">ToString::to_string</span>)
+                    .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>()
+            );
+
+            <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="number">3</span>, <span class="ident">cases</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>, <span class="string">&quot;3&quot;</span>]), <span class="ident">cases</span>[<span class="number">0</span>].<span class="ident">args</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;11&quot;</span>, <span class="string">&quot;12&quot;</span>, <span class="string">&quot;13&quot;</span>]), <span class="ident">cases</span>[<span class="number">1</span>].<span class="ident">args</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;21&quot;</span>, <span class="string">&quot;22&quot;</span>, <span class="string">&quot;23&quot;</span>]), <span class="ident">cases</span>[<span class="number">2</span>].<span class="ident">args</span>());
+        }
+
+        <span class="kw">mod</span> <span class="ident">defined_via_with_attributes</span> {
+            <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">one_case</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                #[case::first(42, &quot;first&quot;)]
+                fn test_fn(#[case] arg1: u32, #[case] arg2: &amp;str) {
+                }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="kw">let</span> <span class="ident">case_args</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">case_args</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+                <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="macro">to_idents!</span>([<span class="string">&quot;arg1&quot;</span>, <span class="string">&quot;arg2&quot;</span>]), <span class="ident">case_args</span>);
+                <span class="macro">assert_eq!</span>(
+                    <span class="macro">vec!</span>[
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;first&quot;&quot;#</span>].<span class="ident">iter</span>()).<span class="ident">with_description</span>(<span class="string">&quot;first&quot;</span>),
+                    ],
+                    <span class="ident">cases</span>
+                );
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">parse_tuple_value</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                #[case(42, (24, &quot;first&quot;))]
+                fn test_fn(#[case] arg1: u32, #[case] tupled: (u32, &amp;str)) {
+                }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+                <span class="macro">assert_eq!</span>(
+                    <span class="macro">vec!</span>[<span class="ident">TestCase::from_iter</span>([<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;(24, &quot;first&quot;)&quot;#</span>].<span class="ident">iter</span>()),],
+                    <span class="ident">cases</span>
+                );
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">more_cases</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                #[case::first(42, &quot;first&quot;)]
+                #[case(24, &quot;second&quot;)]
+                #[case::third(0, &quot;third&quot;)]
+                fn test_fn(#[case] arg1: u32, #[case] arg2: &amp;str) {
+                }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="kw">let</span> <span class="ident">case_args</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">case_args</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+                <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="macro">to_idents!</span>([<span class="string">&quot;arg1&quot;</span>, <span class="string">&quot;arg2&quot;</span>]), <span class="ident">case_args</span>);
+                <span class="macro">assert_eq!</span>(
+                    <span class="macro">vec!</span>[
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;first&quot;&quot;#</span>].<span class="ident">iter</span>()).<span class="ident">with_description</span>(<span class="string">&quot;first&quot;</span>),
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;24&quot;</span>, <span class="string">r#&quot;&quot;second&quot;&quot;#</span>].<span class="ident">iter</span>()),
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;0&quot;</span>, <span class="string">r#&quot;&quot;third&quot;&quot;#</span>].<span class="ident">iter</span>()).<span class="ident">with_description</span>(<span class="string">&quot;third&quot;</span>),
+                    ],
+                    <span class="ident">cases</span>
+                );
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">should_collect_attributes</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[first]
+                    #[first2(42)]
+                    #[case(42)]
+                    #[second]
+                    #[case(24)]
+                    #[global]
+                    fn test_fn(#[case] arg: u32) {
+                    }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+                <span class="macro">assert_eq!</span>(
+                    <span class="macro">vec!</span>[
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;42&quot;</span>].<span class="ident">iter</span>()).<span class="ident">with_attrs</span>(<span class="ident">attrs</span>(
+                            <span class="string">&quot;
+                                #[first]
+                                #[first2(42)]
+                            &quot;</span>
+                        )),
+                        <span class="ident">TestCase::from_iter</span>([<span class="string">&quot;24&quot;</span>].<span class="ident">iter</span>()).<span class="ident">with_attrs</span>(<span class="ident">attrs</span>(
+                            <span class="string">&quot;
+                            #[second]
+                        &quot;</span>
+                        )),
+                    ],
+                    <span class="ident">cases</span>
+                );
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">should_consume_all_used_attributes</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[first]
+                    #[first2(42)]
+                    #[case(42)]
+                    #[second]
+                    #[case(24)]
+                    #[global]
+                    fn test_fn(#[case] arg: u32) {
+                    }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="macro">assert_eq!</span>(
+                    <span class="ident">item_fn</span>.<span class="ident">attrs</span>,
+                    <span class="ident">attrs</span>(
+                        <span class="string">&quot;
+                        #[global]
+                        &quot;</span>
+                    )
+                );
+                <span class="macro">assert!</span>(<span class="op">!</span><span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">item_fn</span>).<span class="ident">contains</span>(<span class="string">&quot;case&quot;</span>));
+            }
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">should_report_all_errors</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                    #[case(#case_error#)]
+                    fn test_fn(#[case] arg: u32, #[with(#fixture_error#)] err_fixture: u32) {
+                    }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="kw">let</span> <span class="ident">errors</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap_err</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">errors</span>.<span class="ident">len</span>());
+            }
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">should_accept_comma_at_the_end_of_cases</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                arg,
+                case(42),
+            &quot;#</span>,
+            )
+            .<span class="ident">data</span>;
+
+            <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">case_args</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">args</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">cases</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="string">&quot;arg&quot;</span>, <span class="kw-2">&amp;</span><span class="ident">args</span>[<span class="number">0</span>].<span class="ident">to_string</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>]), <span class="ident">cases</span>[<span class="number">0</span>].<span class="ident">args</span>())
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="attribute">#[<span class="ident">should_panic</span>]</span>
+        <span class="kw">fn</span> <span class="ident">should_not_accept_invalid_separator_from_args_and_cases</span>() {
+            <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                ret
+                case::should_success(Ok(())),
+                case::should_fail(Err(&quot;Return Error&quot;))
+            &quot;#</span>,
+            );
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">case_could_be_arg_name</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                case,
+                case(42)
+            &quot;#</span>,
+            )
+            .<span class="ident">data</span>;
+
+            <span class="macro">assert_eq!</span>(<span class="string">&quot;case&quot;</span>, <span class="kw-2">&amp;</span><span class="ident">data</span>.<span class="ident">case_args</span>().<span class="ident">next</span>().<span class="ident">unwrap</span>().<span class="ident">to_string</span>());
+
+            <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="number">1</span>, <span class="ident">cases</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>]), <span class="ident">cases</span>[<span class="number">0</span>].<span class="ident">args</span>());
+        }
+    }
+
+    <span class="kw">mod</span> <span class="ident">matrix_cases</span> {
+        <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse::Attribute</span>;
+
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">happy_path</span>() {
+            <span class="kw">let</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                    expected =&gt; [12, 34 * 2],
+                    input =&gt; [format!(&quot;aa_{}&quot;, 2), &quot;other&quot;],
+                &quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">value_ranges</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">list_values</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">value_ranges</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;12&quot;</span>, <span class="string">&quot;34 * 2&quot;</span>]), <span class="ident">value_ranges</span>[<span class="number">0</span>].<span class="ident">args</span>());
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">to_args!</span>([<span class="string">r#&quot;format!(&quot;aa_{}&quot;, 2)&quot;#</span>, <span class="string">r#&quot;&quot;other&quot;&quot;#</span>]),
+                <span class="ident">value_ranges</span>[<span class="number">1</span>].<span class="ident">args</span>()
+            );
+            <span class="macro">assert_eq!</span>(<span class="ident">info</span>.<span class="ident">attributes</span>, <span class="ident">Default::default</span>());
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">should_parse_attributes_too</span>() {
+            <span class="kw">let</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                                        a =&gt; [12, 24, 42]
+                                        ::trace
+                                    &quot;#</span>,
+            );
+
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">info</span>.<span class="ident">attributes</span>,
+                <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::attr</span>(<span class="string">&quot;trace&quot;</span>)]
+                }
+                .<span class="ident">into</span>()
+            );
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">should_parse_injected_fixtures_too</span>() {
+            <span class="kw">let</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                a =&gt; [12, 24, 42],
+                fixture_1(42, &quot;foo&quot;),
+                fixture_2(&quot;bar&quot;)
+                &quot;#</span>,
+            );
+
+            <span class="kw">let</span> <span class="ident">fixtures</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">fixtures</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">vec!</span>[
+                    <span class="ident">fixture</span>(<span class="string">&quot;fixture_1&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;foo&quot;&quot;#</span>]),
+                    <span class="ident">fixture</span>(<span class="string">&quot;fixture_2&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">r#&quot;&quot;bar&quot;&quot;#</span>])
+                ],
+                <span class="ident">fixtures</span>
+            );
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="attribute">#[<span class="ident">should_panic</span>(<span class="ident">expected</span> <span class="op">=</span> <span class="string">&quot;should not be empty&quot;</span>)]</span>
+        <span class="kw">fn</span> <span class="ident">should_not_compile_if_empty_expression_slice</span>() {
+            <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                invalid =&gt; []
+                &quot;#</span>,
+            );
+        }
+
+        <span class="kw">mod</span> <span class="ident">defined_via_with_attributes</span> {
+            <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+            <span class="attribute">#[<span class="ident">test</span>]</span>
+            <span class="kw">fn</span> <span class="ident">one_arg</span>() {
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="string">r#&quot;
+                fn test_fn(#[values(1, 2, 1+2)] arg1: u32, #[values(format!(&quot;a&quot;), &quot;b b&quot;.to_owned(), String::new())] arg2: String) {
+                }
+                &quot;#</span>
+                .<span class="ident">ast</span>();
+
+                <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">info</span> <span class="op">=</span> <span class="ident">RsTestInfo::default</span>();
+
+                <span class="ident">info</span>.<span class="ident">extend_with_function_attrs</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">item_fn</span>).<span class="ident">unwrap</span>();
+
+                <span class="kw">let</span> <span class="ident">list_values</span> <span class="op">=</span> <span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">list_values</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+                <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">list_values</span>.<span class="ident">len</span>());
+                <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>, <span class="string">&quot;1+2&quot;</span>]), <span class="ident">list_values</span>[<span class="number">0</span>].<span class="ident">args</span>());
+                <span class="macro">assert_eq!</span>(
+                    <span class="macro">to_args!</span>([<span class="string">r#&quot;format!(&quot;a&quot;)&quot;#</span>, <span class="string">r#&quot;&quot;b b&quot;.to_owned()&quot;#</span>, <span class="string">&quot;String::new()&quot;</span>]),
+                    <span class="ident">list_values</span>[<span class="number">1</span>].<span class="ident">args</span>()
+                );
+            }
+        }
+    }
+
+    <span class="kw">mod</span> <span class="ident">integrated</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">should_parse_fixture_cases_and_matrix_in_any_order</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="ident">parse_rstest</span>(
+                <span class="string">r#&quot;
+                u,
+                m =&gt; [1, 2],
+                case(42, A{}, D{}),
+                a,
+                case(43, A{}, D{}),
+                the_fixture(42),
+                mm =&gt; [&quot;f&quot;, &quot;oo&quot;, &quot;BAR&quot;],
+                d
+            &quot;#</span>,
+            )
+            .<span class="ident">data</span>;
+
+            <span class="kw">let</span> <span class="ident">fixtures</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">fixtures</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="macro">assert_eq!</span>(<span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;the_fixture&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;42&quot;</span>])], <span class="ident">fixtures</span>);
+
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">to_strs!</span>(<span class="macro">vec!</span>[<span class="string">&quot;u&quot;</span>, <span class="string">&quot;a&quot;</span>, <span class="string">&quot;d&quot;</span>]),
+                <span class="ident">data</span>.<span class="ident">case_args</span>()
+                    .<span class="ident">map</span>(<span class="ident">ToString::to_string</span>)
+                    .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>()
+            );
+
+            <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">cases</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>, <span class="string">&quot;A{}&quot;</span>, <span class="string">&quot;D{}&quot;</span>]), <span class="ident">cases</span>[<span class="number">0</span>].<span class="ident">args</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;43&quot;</span>, <span class="string">&quot;A{}&quot;</span>, <span class="string">&quot;D{}&quot;</span>]), <span class="ident">cases</span>[<span class="number">1</span>].<span class="ident">args</span>());
+
+            <span class="kw">let</span> <span class="ident">value_ranges</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">list_values</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">value_ranges</span>.<span class="ident">len</span>());
+            <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>]), <span class="ident">value_ranges</span>[<span class="number">0</span>].<span class="ident">args</span>());
+            <span class="macro">assert_eq!</span>(
+                <span class="macro">to_args!</span>([<span class="string">r#&quot;&quot;f&quot;&quot;#</span>, <span class="string">r#&quot;&quot;oo&quot;&quot;#</span>, <span class="string">r#&quot;&quot;BAR&quot;&quot;#</span>]),
+                <span class="ident">value_ranges</span>[<span class="number">1</span>].<span class="ident">args</span>()
+            );
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/testcase.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/testcase.rs.html
new file mode 100644
index 0000000..fd9feefa
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/testcase.rs.html
@@ -0,0 +1,331 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/testcase.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>testcase.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">syn</span>::{
+    <span class="ident">parse</span>::{<span class="ident">Error</span>, <span class="ident">Parse</span>, <span class="ident">ParseStream</span>, <span class="prelude-ty">Result</span>},
+    <span class="ident">punctuated::Punctuated</span>,
+    <span class="ident">Attribute</span>, <span class="ident">Expr</span>, <span class="ident">Ident</span>, <span class="ident">Token</span>,
+};
+
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">quote::ToTokens</span>;
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">PartialEq</span>, <span class="ident">Debug</span>, <span class="ident">Clone</span>)]</span>
+<span class="doccomment">/// A test case instance data. Contains a list of arguments. It is parsed by parametrize</span>
+<span class="doccomment">/// attributes.</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">TestCase</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">args</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">attrs</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Attribute</span><span class="op">&gt;</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">description</span>: <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">TestCase</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">Attribute::parse_outer</span>(<span class="ident">input</span>)<span class="question-mark">?</span>;
+        <span class="kw">let</span> <span class="ident">case</span>: <span class="ident">Ident</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">if</span> <span class="ident">case</span> <span class="op">=</span><span class="op">=</span> <span class="string">&quot;case&quot;</span> {
+            <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">description</span> <span class="op">=</span> <span class="prelude-val">None</span>;
+            <span class="kw">if</span> <span class="ident">input</span>.<span class="ident">peek</span>(<span class="macro">Token!</span>[::]) {
+                <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>::<span class="op">&lt;</span><span class="macro">Token!</span>[::]<span class="op">&gt;</span>();
+                <span class="ident">description</span> <span class="op">=</span> <span class="prelude-val">Some</span>(<span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>);
+            }
+            <span class="kw">let</span> <span class="ident">content</span>;
+            <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="macro">syn::parenthesized!</span>(<span class="ident">content</span> <span class="kw">in</span> <span class="ident">input</span>);
+            <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">Punctuated</span>::<span class="op">&lt;</span><span class="ident">Expr</span>, <span class="macro">Token!</span>[,]<span class="op">&gt;</span><span class="ident">::parse_terminated</span>(<span class="kw-2">&amp;</span><span class="ident">content</span>)<span class="question-mark">?</span>
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">collect</span>();
+            <span class="prelude-val">Ok</span>(<span class="ident">TestCase</span> {
+                <span class="ident">args</span>,
+                <span class="ident">attrs</span>,
+                <span class="ident">description</span>,
+            })
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">Error::new</span>(<span class="ident">case</span>.<span class="ident">span</span>(), <span class="string">&quot;expected a test case&quot;</span>))
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ToTokens</span> <span class="kw">for</span> <span class="ident">TestCase</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="self">self</span>.<span class="ident">args</span>.<span class="ident">iter</span>().<span class="ident">for_each</span>(<span class="op">|</span><span class="ident">c</span><span class="op">|</span> <span class="ident">c</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>))
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+    <span class="kw">fn</span> <span class="ident">parse_test_case</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">test_case</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TestCase</span> {
+        <span class="ident">parse_meta</span>(<span class="ident">test_case</span>)
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">two_literal_args</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="string">r#&quot;case(42, &quot;value&quot;)&quot;#</span>);
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>, <span class="string">r#&quot;&quot;value&quot;&quot;#</span>]);
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">args</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">some_literals</span>() {
+        <span class="kw">let</span> <span class="ident">args_expressions</span> <span class="op">=</span> <span class="ident">literal_expressions_str</span>();
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(<span class="string">&quot;case({})&quot;</span>, <span class="ident">args_expressions</span>.<span class="ident">join</span>(<span class="string">&quot;, &quot;</span>)));
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>(<span class="ident">args_expressions</span>), <span class="ident">args</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">accept_arbitrary_rust_code</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="string">r#&quot;case(vec![1,2,3])&quot;#</span>);
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;vec![1, 2, 3]&quot;</span>]), <span class="ident">args</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="attribute">#[<span class="ident">should_panic</span>]</span>
+    <span class="kw">fn</span> <span class="ident">raise_error_on_invalid_rust_code</span>() {
+        <span class="ident">parse_test_case</span>(<span class="string">r#&quot;case(some:&lt;&gt;(1,2,3))&quot;#</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">get_description_if_any</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="string">r#&quot;case::this_test_description(42)&quot;#</span>);
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="macro">assert_eq!</span>(
+            <span class="string">&quot;this_test_description&quot;</span>,
+            <span class="kw-2">&amp;</span><span class="ident">test_case</span>.<span class="ident">description</span>.<span class="ident">unwrap</span>().<span class="ident">to_string</span>()
+        );
+        <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>]), <span class="ident">args</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">get_description_also_with_more_args</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="string">r#&quot;case :: this_test_description (42, 24)&quot;#</span>);
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="macro">assert_eq!</span>(
+            <span class="string">&quot;this_test_description&quot;</span>,
+            <span class="kw-2">&amp;</span><span class="ident">test_case</span>.<span class="ident">description</span>.<span class="ident">unwrap</span>().<span class="ident">to_string</span>()
+        );
+        <span class="macro">assert_eq!</span>(<span class="macro">to_args!</span>([<span class="string">&quot;42&quot;</span>, <span class="string">&quot;24&quot;</span>]), <span class="ident">args</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">parse_arbitrary_rust_code_as_expression</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(
+            <span class="string">r##&quot;
+            case(42, -42,
+            pippo(&quot;pluto&quot;),
+            Vec::new(),
+            String::from(r#&quot;prrr&quot;#),
+            {
+                let mut sum=0;
+                for i in 1..3 {
+                    sum += i;
+                }
+                sum
+            },
+            vec![1,2,3]
+        )&quot;##</span>,
+        );
+
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test_case</span>.<span class="ident">args</span>();
+
+        <span class="macro">assert_eq!</span>(
+            <span class="macro">to_args!</span>([
+                <span class="string">&quot;42&quot;</span>,
+                <span class="string">&quot;-42&quot;</span>,
+                <span class="string">r#&quot;pippo(&quot;pluto&quot;)&quot;#</span>,
+                <span class="string">&quot;Vec::new()&quot;</span>,
+                <span class="string">r##&quot;String::from(r#&quot;prrr&quot;#)&quot;##</span>,
+                <span class="string">r#&quot;{let mut sum=0;for i in 1..3 {sum += i;}sum}&quot;#</span>,
+                <span class="string">&quot;vec![1,2,3]&quot;</span>
+            ]),
+            <span class="ident">args</span>
+        );
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">save_attributes</span>() {
+        <span class="kw">let</span> <span class="ident">test_case</span> <span class="op">=</span> <span class="ident">parse_test_case</span>(<span class="string">r#&quot;#[should_panic]#[other_attr(x)]case(42)&quot;#</span>);
+
+        <span class="kw">let</span> <span class="ident">content</span> <span class="op">=</span> <span class="macro">format!</span>(<span class="string">&quot;{:?}&quot;</span>, <span class="ident">test_case</span>.<span class="ident">attrs</span>);
+
+        <span class="macro">assert_eq!</span>(<span class="number">2</span>, <span class="ident">test_case</span>.<span class="ident">attrs</span>.<span class="ident">len</span>());
+        <span class="macro">assert!</span>(<span class="ident">content</span>.<span class="ident">contains</span>(<span class="string">&quot;should_panic&quot;</span>));
+        <span class="macro">assert!</span>(<span class="ident">content</span>.<span class="ident">contains</span>(<span class="string">&quot;other_attr&quot;</span>));
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/vlist.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/vlist.rs.html
new file mode 100644
index 0000000..99253c2f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/parse/vlist.rs.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/parse/vlist.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>vlist.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">quote::ToTokens</span>;
+<span class="kw">use</span> <span class="ident">syn</span>::{
+    <span class="ident">parse</span>::{<span class="ident">Parse</span>, <span class="ident">ParseStream</span>, <span class="prelude-ty">Result</span>},
+    <span class="ident">Expr</span>, <span class="ident">Ident</span>, <span class="ident">Token</span>,
+};
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::refident::RefIdent</span>;
+
+<span class="kw">use</span> <span class="kw">super</span><span class="ident">::expressions::Expressions</span>;
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Debug</span>, <span class="ident">PartialEq</span>, <span class="ident">Clone</span>)]</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct</span> <span class="ident">ValueList</span> {
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">arg</span>: <span class="ident">Ident</span>,
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="ident">values</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">ValueList</span> {
+    <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">arg</span> <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">let</span> <span class="ident">_to</span>: <span class="macro">Token!</span>[<span class="op">=</span><span class="op">&gt;</span>] <span class="op">=</span> <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+        <span class="kw">let</span> <span class="ident">content</span>;
+        <span class="kw">let</span> <span class="ident">paren</span> <span class="op">=</span> <span class="macro">syn::bracketed!</span>(<span class="ident">content</span> <span class="kw">in</span> <span class="ident">input</span>);
+        <span class="kw">let</span> <span class="ident">values</span>: <span class="ident">Expressions</span> <span class="op">=</span> <span class="ident">content</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>;
+
+        <span class="kw">let</span> <span class="ident">ret</span> <span class="op">=</span> <span class="self">Self</span> {
+            <span class="ident">arg</span>,
+            <span class="ident">values</span>: <span class="ident">values</span>.<span class="ident">take</span>(),
+        };
+        <span class="kw">if</span> <span class="ident">ret</span>.<span class="ident">values</span>.<span class="ident">len</span>() <span class="op">=</span><span class="op">=</span> <span class="number">0</span> {
+            <span class="prelude-val">Err</span>(<span class="ident">syn::Error::new</span>(
+                <span class="ident">paren</span>.<span class="ident">span</span>,
+                <span class="string">&quot;Values list should not be empty&quot;</span>,
+            ))
+        } <span class="kw">else</span> {
+            <span class="prelude-val">Ok</span>(<span class="ident">ret</span>)
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">RefIdent</span> <span class="kw">for</span> <span class="ident">ValueList</span> {
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span> {
+        <span class="kw-2">&amp;</span><span class="self">self</span>.<span class="ident">arg</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">ToTokens</span> <span class="kw">for</span> <span class="ident">ValueList</span> {
+    <span class="kw">fn</span> <span class="ident">to_tokens</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">tokens</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">TokenStream</span>) {
+        <span class="self">self</span>.<span class="ident">arg</span>.<span class="ident">to_tokens</span>(<span class="ident">tokens</span>)
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+    <span class="kw">mod</span> <span class="ident">parse_values_list</span> {
+        <span class="kw">use</span> <span class="kw">super</span><span class="ident">::assert_eq</span>;
+        <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+        <span class="kw">fn</span> <span class="ident">parse_values_list</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">values_list</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">ValueList</span> {
+            <span class="ident">parse_meta</span>(<span class="ident">values_list</span>)
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">some_literals</span>() {
+            <span class="kw">let</span> <span class="ident">literals</span> <span class="op">=</span> <span class="ident">literal_expressions_str</span>();
+            <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="string">&quot;argument&quot;</span>;
+
+            <span class="kw">let</span> <span class="ident">values_list</span> <span class="op">=</span> <span class="ident">parse_values_list</span>(<span class="macro">format!</span>(
+                <span class="string">r#&quot;{} =&gt; [{}]&quot;#</span>,
+                <span class="ident">name</span>,
+                <span class="ident">literals</span>
+                    .<span class="ident">iter</span>()
+                    .<span class="ident">map</span>(<span class="ident">ToString::to_string</span>)
+                    .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="ident">String</span><span class="op">&gt;</span><span class="op">&gt;</span>()
+                    .<span class="ident">join</span>(<span class="string">&quot;, &quot;</span>)
+            ));
+
+            <span class="macro">assert_eq!</span>(<span class="ident">name</span>, <span class="kw-2">&amp;</span><span class="ident">values_list</span>.<span class="ident">arg</span>.<span class="ident">to_string</span>());
+            <span class="macro">assert_eq!</span>(<span class="ident">values_list</span>.<span class="ident">args</span>(), <span class="macro">to_args!</span>(<span class="ident">literals</span>));
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">raw_code</span>() {
+            <span class="kw">let</span> <span class="ident">values_list</span> <span class="op">=</span> <span class="ident">parse_values_list</span>(<span class="string">r#&quot;no_mater =&gt; [vec![1,2,3]]&quot;#</span>);
+
+            <span class="macro">assert_eq!</span>(<span class="ident">values_list</span>.<span class="ident">args</span>(), <span class="macro">to_args!</span>([<span class="string">&quot;vec![1, 2, 3]&quot;</span>]));
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="attribute">#[<span class="ident">should_panic</span>]</span>
+        <span class="kw">fn</span> <span class="ident">raw_code_with_parsing_error</span>() {
+            <span class="ident">parse_values_list</span>(<span class="string">r#&quot;other =&gt; [some:&lt;&gt;(1,2,3)]&quot;#</span>);
+        }
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="attribute">#[<span class="ident">should_panic</span>(<span class="ident">expected</span> <span class="op">=</span> <span class="string">r#&quot;expected square brackets&quot;#</span>)]</span>
+        <span class="kw">fn</span> <span class="ident">forget_brackets</span>() {
+            <span class="ident">parse_values_list</span>(<span class="string">r#&quot;other =&gt; 42&quot;#</span>);
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/refident.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/refident.rs.html
new file mode 100644
index 0000000..5a2c34e7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/refident.rs.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/refident.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>refident.rs - source</title><link rel="stylesheet" type="text/css" href="../../normalize.css"><link rel="stylesheet" type="text/css" href="../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../ayu.css" disabled ><script id="default-settings"></script><script src="../../storage.js"></script><script src="../../crates.js"></script><noscript><link rel="stylesheet" href="../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../settings.html"><img src="../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1"> 1</span>
+<span id="2"> 2</span>
+<span id="3"> 3</span>
+<span id="4"> 4</span>
+<span id="5"> 5</span>
+<span id="6"> 6</span>
+<span id="7"> 7</span>
+<span id="8"> 8</span>
+<span id="9"> 9</span>
+<span id="10">10</span>
+<span id="11">11</span>
+<span id="12">12</span>
+<span id="13">13</span>
+<span id="14">14</span>
+<span id="15">15</span>
+<span id="16">16</span>
+<span id="17">17</span>
+<span id="18">18</span>
+<span id="19">19</span>
+<span id="20">20</span>
+<span id="21">21</span>
+<span id="22">22</span>
+<span id="23">23</span>
+<span id="24">24</span>
+<span id="25">25</span>
+<span id="26">26</span>
+<span id="27">27</span>
+<span id="28">28</span>
+<span id="29">29</span>
+<span id="30">30</span>
+<span id="31">31</span>
+<span id="32">32</span>
+<span id="33">33</span>
+<span id="34">34</span>
+<span id="35">35</span>
+<span id="36">36</span>
+<span id="37">37</span>
+<span id="38">38</span>
+<span id="39">39</span>
+<span id="40">40</span>
+<span id="41">41</span>
+<span id="42">42</span>
+<span id="43">43</span>
+<span id="44">44</span>
+<span id="45">45</span>
+<span id="46">46</span>
+<span id="47">47</span>
+<span id="48">48</span>
+<span id="49">49</span>
+<span id="50">50</span>
+<span id="51">51</span>
+<span id="52">52</span>
+<span id="53">53</span>
+<span id="54">54</span>
+<span id="55">55</span>
+<span id="56">56</span>
+<span id="57">57</span>
+<span id="58">58</span>
+<span id="59">59</span>
+<span id="60">60</span>
+<span id="61">61</span>
+<span id="62">62</span>
+<span id="63">63</span>
+<span id="64">64</span>
+<span id="65">65</span>
+<span id="66">66</span>
+<span id="67">67</span>
+<span id="68">68</span>
+<span id="69">69</span>
+<span id="70">70</span>
+<span id="71">71</span>
+<span id="72">72</span>
+<span id="73">73</span>
+<span id="74">74</span>
+<span id="75">75</span>
+<span id="76">76</span>
+<span id="77">77</span>
+</pre><pre class="rust">
+<span class="doccomment">/// Provide `RefIdent` and `MaybeIdent` traits that give a shortcut to extract identity reference</span>
+<span class="doccomment">/// (`syn::Ident` struct).</span>
+<span class="kw">use</span> <span class="ident">proc_macro2::Ident</span>;
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">FnArg</span>, <span class="ident">Pat</span>, <span class="ident">PatType</span>, <span class="ident">Type</span>};
+
+<span class="kw">pub</span> <span class="kw">trait</span> <span class="ident">RefIdent</span> {
+    <span class="doccomment">/// Return the reference to ident if any</span>
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span>;
+}
+
+<span class="kw">pub</span> <span class="kw">trait</span> <span class="ident">MaybeIdent</span> {
+    <span class="doccomment">/// Return the reference to ident if any</span>
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span>;
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">I</span>: <span class="ident">RefIdent</span><span class="op">&gt;</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">I</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="prelude-val">Some</span>(<span class="self">self</span>.<span class="ident">ident</span>())
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">RefIdent</span> <span class="kw">for</span> <span class="ident">Ident</span> {
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span> {
+        <span class="self">self</span>
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> <span class="ident">RefIdent</span> <span class="kw">for</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">Ident</span> {
+    <span class="kw">fn</span> <span class="ident">ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span> {
+        <span class="kw-2">*</span><span class="self">self</span>
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">FnArg</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">FnArg::Typed</span>(<span class="ident">PatType</span> { <span class="ident">pat</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="kw">match</span> <span class="ident">pat</span>.<span class="ident">as_ref</span>() {
+                <span class="ident">Pat::Ident</span>(<span class="ident">ident</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>.<span class="ident">ident</span>),
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+            },
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">Type</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">Type::Path</span>(<span class="ident">tp</span>) <span class="kw">if</span> <span class="ident">tp</span>.<span class="ident">qself</span>.<span class="ident">is_none</span>() <span class="op">=</span><span class="op">&gt;</span> <span class="ident">tp</span>.<span class="ident">path</span>.<span class="ident">get_ident</span>(),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        }
+    }
+}
+
+<span class="kw">pub</span> <span class="kw">trait</span> <span class="ident">MaybeType</span> {
+    <span class="doccomment">/// Return the reference to type if any</span>
+    <span class="kw">fn</span> <span class="ident">maybe_type</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Type</span><span class="op">&gt;</span>;
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeType</span> <span class="kw">for</span> <span class="ident">FnArg</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_type</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Type</span><span class="op">&gt;</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">FnArg::Typed</span>(<span class="ident">PatType</span> { <span class="ident">ty</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">ty</span>.<span class="ident">as_ref</span>()),
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        }
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">syn::GenericParam</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">syn::GenericParam::Type</span>(<span class="ident">syn::TypeParam</span> { <span class="ident">ident</span>, .. })
+            <span class="op">|</span> <span class="ident">syn::GenericParam::Const</span>(<span class="ident">syn::ConstParam</span> { <span class="ident">ident</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">ident</span>),
+            <span class="ident">syn::GenericParam::Lifetime</span>(<span class="ident">syn::LifetimeDef</span> { <span class="ident">lifetime</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="kw-2">&amp;</span><span class="ident">lifetime</span>.<span class="ident">ident</span>),
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../" data-current-crate="rstest" data-search-index-js="../../search-index.js" data-search-js="../../search.js"></div>
+    <script src="../../main.js"></script><script src="../../source-script.js"></script><script src="../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/fixture.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/fixture.rs.html
new file mode 100644
index 0000000..8cba8144
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/fixture.rs.html
@@ -0,0 +1,881 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/render/fixture.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>fixture.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+<span id="370">370</span>
+<span id="371">371</span>
+<span id="372">372</span>
+<span id="373">373</span>
+<span id="374">374</span>
+<span id="375">375</span>
+<span id="376">376</span>
+<span id="377">377</span>
+<span id="378">378</span>
+<span id="379">379</span>
+<span id="380">380</span>
+<span id="381">381</span>
+<span id="382">382</span>
+<span id="383">383</span>
+<span id="384">384</span>
+<span id="385">385</span>
+<span id="386">386</span>
+<span id="387">387</span>
+<span id="388">388</span>
+<span id="389">389</span>
+<span id="390">390</span>
+<span id="391">391</span>
+<span id="392">392</span>
+<span id="393">393</span>
+<span id="394">394</span>
+<span id="395">395</span>
+<span id="396">396</span>
+<span id="397">397</span>
+<span id="398">398</span>
+<span id="399">399</span>
+<span id="400">400</span>
+<span id="401">401</span>
+<span id="402">402</span>
+<span id="403">403</span>
+<span id="404">404</span>
+<span id="405">405</span>
+<span id="406">406</span>
+<span id="407">407</span>
+<span id="408">408</span>
+<span id="409">409</span>
+<span id="410">410</span>
+<span id="411">411</span>
+<span id="412">412</span>
+<span id="413">413</span>
+<span id="414">414</span>
+<span id="415">415</span>
+<span id="416">416</span>
+<span id="417">417</span>
+<span id="418">418</span>
+<span id="419">419</span>
+<span id="420">420</span>
+<span id="421">421</span>
+<span id="422">422</span>
+<span id="423">423</span>
+<span id="424">424</span>
+<span id="425">425</span>
+<span id="426">426</span>
+<span id="427">427</span>
+<span id="428">428</span>
+<span id="429">429</span>
+<span id="430">430</span>
+<span id="431">431</span>
+<span id="432">432</span>
+<span id="433">433</span>
+<span id="434">434</span>
+<span id="435">435</span>
+<span id="436">436</span>
+<span id="437">437</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">proc_macro2</span>::{<span class="ident">Span</span>, <span class="ident">TokenStream</span>};
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">parse_quote</span>, <span class="ident">Ident</span>, <span class="ident">ItemFn</span>};
+
+<span class="kw">use</span> <span class="ident">quote::quote</span>;
+
+<span class="kw">use</span> <span class="kw">super</span>::{<span class="ident">inject</span>, <span class="ident">render_exec_call</span>};
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::resolver</span>::{<span class="self">self</span>, <span class="ident">Resolver</span>};
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::utils</span>::{<span class="ident">fn_args</span>, <span class="ident">fn_args_idents</span>};
+<span class="kw">use</span> <span class="kw">crate</span>::{<span class="ident">parse::fixture::FixtureInfo</span>, <span class="ident">utils::generics_clean_up</span>};
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">render</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(<span class="ident">fixture</span>: <span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="ident">FixtureInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">ident</span>;
+    <span class="kw">let</span> <span class="ident">asyncness</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">asyncness</span>.<span class="ident">clone</span>();
+    <span class="kw">let</span> <span class="ident">vargs</span> <span class="op">=</span> <span class="ident">fn_args_idents</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>).<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">vargs</span>;
+    <span class="kw">let</span> <span class="ident">orig_args</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>;
+    <span class="kw">let</span> <span class="ident">orig_attrs</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">attrs</span>;
+    <span class="kw">let</span> <span class="ident">generics</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">generics</span>;
+    <span class="kw">let</span> <span class="ident">default_output</span> <span class="op">=</span> <span class="ident">info</span>
+        .<span class="ident">attributes</span>
+        .<span class="ident">extract_default_type</span>()
+        .<span class="ident">unwrap_or</span>(<span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">output</span>.<span class="ident">clone</span>());
+    <span class="kw">let</span> <span class="ident">default_generics</span> <span class="op">=</span>
+        <span class="ident">generics_clean_up</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">generics</span>, <span class="ident">std::iter::empty</span>(), <span class="kw-2">&amp;</span><span class="ident">default_output</span>);
+    <span class="kw">let</span> <span class="ident">default_where_clause</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">default_generics</span>.<span class="ident">where_clause</span>;
+    <span class="kw">let</span> <span class="ident">where_clause</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">generics</span>.<span class="ident">where_clause</span>;
+    <span class="kw">let</span> <span class="ident">output</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">output</span>;
+    <span class="kw">let</span> <span class="ident">visibility</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">vis</span>;
+    <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> (
+        <span class="ident">resolver::fixtures::get</span>(<span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">fixtures</span>()),
+        <span class="ident">resolver::values::get</span>(<span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">values</span>()),
+    );
+    <span class="kw">let</span> <span class="ident">generics_idents</span> <span class="op">=</span> <span class="ident">generics</span>
+        .<span class="ident">type_params</span>()
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">tp</span><span class="op">|</span> <span class="kw-2">&amp;</span><span class="ident">tp</span>.<span class="ident">ident</span>)
+        .<span class="ident">cloned</span>()
+        .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">inject</span> <span class="op">=</span> <span class="ident">inject::resolve_aruments</span>(<span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>(), <span class="kw-2">&amp;</span><span class="ident">resolver</span>, <span class="kw-2">&amp;</span><span class="ident">generics_idents</span>);
+    <span class="kw">let</span> <span class="ident">partials</span> <span class="op">=</span>
+        (<span class="number">1</span>..<span class="op">=</span><span class="ident">orig_args</span>.<span class="ident">len</span>()).<span class="ident">map</span>(<span class="op">|</span><span class="ident">n</span><span class="op">|</span> <span class="ident">render_partial_impl</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>, <span class="ident">n</span>, <span class="kw-2">&amp;</span><span class="ident">resolver</span>, <span class="kw-2">&amp;</span><span class="ident">info</span>));
+
+    <span class="kw">let</span> <span class="ident">call_get</span> <span class="op">=</span> <span class="ident">render_exec_call</span>(<span class="macro">parse_quote!</span> { <span class="self">Self</span><span class="ident">::get</span> }, <span class="ident">args</span>, <span class="ident">asyncness</span>.<span class="ident">is_some</span>());
+    <span class="kw">let</span> <span class="ident">call_impl</span> <span class="op">=</span> <span class="ident">render_exec_call</span>(<span class="macro">parse_quote!</span> { #<span class="ident">name</span> }, <span class="ident">args</span>, <span class="ident">asyncness</span>.<span class="ident">is_some</span>());
+
+    <span class="macro">quote!</span> {
+        <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">non_camel_case_types</span>)]</span>
+        #<span class="ident">visibility</span> <span class="kw">struct</span> #<span class="ident">name</span> {}
+
+        <span class="kw">impl</span> #<span class="ident">name</span> {
+            #(#<span class="ident">orig_attrs</span>)<span class="op">*</span>
+            <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">unused_mut</span>)]</span>
+            <span class="kw">pub</span> #<span class="ident">asyncness</span> <span class="kw">fn</span> <span class="ident">get</span> #<span class="ident">generics</span> (#<span class="ident">orig_args</span>) #<span class="ident">output</span> #<span class="ident">where_clause</span> {
+                #<span class="ident">call_impl</span>
+            }
+
+            <span class="kw">pub</span> #<span class="ident">asyncness</span> <span class="kw">fn</span> <span class="ident">default</span> #<span class="ident">default_generics</span> () #<span class="ident">default_output</span> #<span class="ident">default_where_clause</span> {
+                #<span class="ident">inject</span>
+                #<span class="ident">call_get</span>
+            }
+
+            #(#<span class="ident">partials</span>)<span class="op">*</span>
+        }
+
+        <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">dead_code</span>)]</span>
+        #<span class="ident">fixture</span>
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">render_partial_impl</span>(
+    <span class="ident">fixture</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>,
+    <span class="ident">n</span>: <span class="ident">usize</span>,
+    <span class="ident">resolver</span>: <span class="kw-2">&amp;</span><span class="kw">impl</span> <span class="ident">Resolver</span>,
+    <span class="ident">info</span>: <span class="kw-2">&amp;</span><span class="ident">FixtureInfo</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">output</span> <span class="op">=</span> <span class="ident">info</span>
+        .<span class="ident">attributes</span>
+        .<span class="ident">extract_partial_type</span>(<span class="ident">n</span>)
+        .<span class="ident">unwrap_or</span>(<span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">output</span>.<span class="ident">clone</span>());
+
+    <span class="kw">let</span> <span class="ident">generics</span> <span class="op">=</span> <span class="ident">generics_clean_up</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">generics</span>, <span class="ident">fn_args</span>(<span class="ident">fixture</span>).<span class="ident">take</span>(<span class="ident">n</span>), <span class="kw-2">&amp;</span><span class="ident">output</span>);
+    <span class="kw">let</span> <span class="ident">where_clause</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">generics</span>.<span class="ident">where_clause</span>;
+    <span class="kw">let</span> <span class="ident">asyncness</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">asyncness</span>;
+
+    <span class="kw">let</span> <span class="ident">genercs_idents</span> <span class="op">=</span> <span class="ident">generics</span>
+        .<span class="ident">type_params</span>()
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">tp</span><span class="op">|</span> <span class="kw-2">&amp;</span><span class="ident">tp</span>.<span class="ident">ident</span>)
+        .<span class="ident">cloned</span>()
+        .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">inject</span> <span class="op">=</span>
+        <span class="ident">inject::resolve_aruments</span>(<span class="ident">fixture</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>().<span class="ident">skip</span>(<span class="ident">n</span>), <span class="ident">resolver</span>, <span class="kw-2">&amp;</span><span class="ident">genercs_idents</span>);
+
+    <span class="kw">let</span> <span class="ident">sign_args</span> <span class="op">=</span> <span class="ident">fn_args</span>(<span class="ident">fixture</span>).<span class="ident">take</span>(<span class="ident">n</span>);
+    <span class="kw">let</span> <span class="ident">fixture_args</span> <span class="op">=</span> <span class="ident">fn_args_idents</span>(<span class="ident">fixture</span>).<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="ident">Ident::new</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(<span class="string">&quot;partial_{}&quot;</span>, <span class="ident">n</span>), <span class="ident">Span::call_site</span>());
+
+    <span class="kw">let</span> <span class="ident">call_get</span> <span class="op">=</span> <span class="ident">render_exec_call</span>(
+        <span class="macro">parse_quote!</span> { <span class="self">Self</span><span class="ident">::get</span> },
+        <span class="kw-2">&amp;</span><span class="ident">fixture_args</span>,
+        <span class="ident">asyncness</span>.<span class="ident">is_some</span>(),
+    );
+
+    <span class="macro">quote!</span> {
+        <span class="attribute">#[<span class="ident">allow</span>(<span class="ident">unused_mut</span>)]</span>
+        <span class="kw">pub</span> #<span class="ident">asyncness</span> <span class="kw">fn</span> #<span class="ident">name</span> #<span class="ident">generics</span> (#(#<span class="ident">sign_args</span>),<span class="kw-2">*</span>) #<span class="ident">output</span> #<span class="ident">where_clause</span> {
+            #<span class="ident">inject</span>
+            #<span class="ident">call_get</span>
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="ident">syn</span>::{
+        <span class="ident">parse</span>::{<span class="ident">Parse</span>, <span class="ident">ParseStream</span>},
+        <span class="ident">parse2</span>, <span class="ident">parse_str</span>, <span class="ident">ItemFn</span>, <span class="ident">ItemImpl</span>, <span class="ident">ItemStruct</span>, <span class="prelude-ty">Result</span>,
+    };
+
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse</span>::{<span class="ident">Attribute</span>, <span class="ident">Attributes</span>};
+
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test::assert_eq</span>;
+    <span class="kw">use</span> <span class="ident">mytest</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="ident">rstest_reuse</span>::<span class="kw-2">*</span>;
+
+    <span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Clone</span>)]</span>
+    <span class="kw">struct</span> <span class="ident">FixtureOutput</span> {
+        <span class="ident">orig</span>: <span class="ident">ItemFn</span>,
+        <span class="ident">fixture</span>: <span class="ident">ItemStruct</span>,
+        <span class="ident">core_impl</span>: <span class="ident">ItemImpl</span>,
+    }
+
+    <span class="kw">impl</span> <span class="ident">Parse</span> <span class="kw">for</span> <span class="ident">FixtureOutput</span> {
+        <span class="kw">fn</span> <span class="ident">parse</span>(<span class="ident">input</span>: <span class="ident">ParseStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Result</span><span class="op">&lt;</span><span class="self">Self</span><span class="op">&gt;</span> {
+            <span class="prelude-val">Ok</span>(<span class="ident">FixtureOutput</span> {
+                <span class="ident">fixture</span>: <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>,
+                <span class="ident">core_impl</span>: <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>,
+                <span class="ident">orig</span>: <span class="ident">input</span>.<span class="ident">parse</span>()<span class="question-mark">?</span>,
+            })
+        }
+    }
+
+    <span class="kw">fn</span> <span class="ident">parse_fixture</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">code</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> (<span class="ident">ItemFn</span>, <span class="ident">FixtureOutput</span>) {
+        <span class="kw">let</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>(<span class="ident">code</span>.<span class="ident">as_ref</span>()).<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">tokens</span> <span class="op">=</span> <span class="ident">render</span>(<span class="ident">item_fn</span>.<span class="ident">clone</span>(), <span class="ident">Default::default</span>());
+        (<span class="ident">item_fn</span>, <span class="ident">parse2</span>(<span class="ident">tokens</span>).<span class="ident">unwrap</span>())
+    }
+
+    <span class="kw">fn</span> <span class="ident">test_maintains_function_visibility</span>(<span class="ident">code</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+        <span class="kw">let</span> (<span class="ident">item_fn</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="ident">code</span>);
+
+        <span class="macro">assert_eq!</span>(<span class="ident">item_fn</span>.<span class="ident">vis</span>, <span class="ident">out</span>.<span class="ident">fixture</span>.<span class="ident">vis</span>);
+        <span class="macro">assert_eq!</span>(<span class="ident">item_fn</span>.<span class="ident">vis</span>, <span class="ident">out</span>.<span class="ident">orig</span>.<span class="ident">vis</span>);
+    }
+
+    <span class="kw">fn</span> <span class="ident">select_method</span><span class="op">&lt;</span><span class="ident">S</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">str</span><span class="op">&gt;</span><span class="op">&gt;</span>(<span class="ident">impl_code</span>: <span class="ident">ItemImpl</span>, <span class="ident">name</span>: <span class="ident">S</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">syn::ImplItemMethod</span><span class="op">&gt;</span> {
+        <span class="ident">impl_code</span>
+            .<span class="ident">items</span>
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">ii</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">ii</span> {
+                <span class="ident">syn::ImplItem::Method</span>(<span class="ident">f</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="ident">f</span>),
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+            })
+            .<span class="ident">find</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">sig</span>.<span class="ident">ident</span> <span class="op">=</span><span class="op">=</span> <span class="ident">name</span>.<span class="ident">as_ref</span>())
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">maintains_pub_visibility</span>() {
+        <span class="ident">test_maintains_function_visibility</span>(<span class="string">r#&quot;pub fn test() { }&quot;#</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">maintains_no_pub_visibility</span>() {
+        <span class="ident">test_maintains_function_visibility</span>(<span class="string">r#&quot;fn test() { }&quot;#</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">implement_a_get_method_with_input_fixture_signature</span>() {
+        <span class="kw">let</span> (<span class="ident">item_fn</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(
+            <span class="string">r#&quot;
+                    pub fn test&lt;R: AsRef&lt;str&gt;, B&gt;(mut s: String, v: &amp;u32, a: &amp;mut [i32], r: R) -&gt; (u32, B, String, &amp;str)
+                            where B: Borrow&lt;u32&gt;
+                    { }
+                    &quot;#</span>,
+        );
+
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">signature</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="string">&quot;get&quot;</span>).<span class="ident">unwrap</span>().<span class="ident">sig</span>;
+
+        <span class="ident">signature</span>.<span class="ident">ident</span> <span class="op">=</span> <span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">ident</span>.<span class="ident">clone</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">item_fn</span>.<span class="ident">sig</span>, <span class="ident">signature</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">template</span>]</span>
+    <span class="attribute">#[<span class="ident">rstest</span>(
+        <span class="ident">method</span> <span class="op">=</span><span class="op">&gt;</span> [<span class="string">&quot;default&quot;</span>, <span class="string">&quot;get&quot;</span>, <span class="string">&quot;partial_1&quot;</span>, <span class="string">&quot;partial_2&quot;</span>, <span class="string">&quot;partial_3&quot;</span>]</span>)
+    ]
+    <span class="attribute">#[<span class="ident">case::async_fn</span>(<span class="bool-val">true</span>)]</span>
+    <span class="attribute">#[<span class="ident">case::not_async_fn</span>(<span class="bool-val">false</span>)]</span>
+    <span class="kw">fn</span> <span class="ident">async_fixture_cases</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">is_async</span>: <span class="ident">bool</span>, <span class="ident">method</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {}
+
+    <span class="attribute">#[<span class="ident">apply</span>(<span class="ident">async_fixture_cases</span>)]</span>
+    <span class="kw">fn</span> <span class="ident">fixture_method_should_be_async_if_fixture_function_is_async</span>(
+        <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">is_async</span>: <span class="ident">bool</span>,
+        <span class="ident">method</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>,
+    ) {
+        <span class="kw">let</span> <span class="ident">prefix</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">is_async</span> { <span class="string">&quot;async&quot;</span> } <span class="kw">else</span> { <span class="string">&quot;&quot;</span> };
+        <span class="kw">let</span> (<span class="kw">_</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(
+            <span class="string">r#&quot;
+                    pub {} fn test(mut s: String, v: &amp;u32, a: &amp;mut [i32]) -&gt; u32
+                            where B: Borrow&lt;u32&gt;
+                    {{ }}
+                    &quot;#</span>,
+            <span class="ident">prefix</span>
+        ));
+
+        <span class="kw">let</span> <span class="ident">signature</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="ident">method</span>).<span class="ident">unwrap</span>().<span class="ident">sig</span>;
+
+        <span class="macro">assert_eq!</span>(<span class="ident">is_async</span>, <span class="ident">signature</span>.<span class="ident">asyncness</span>.<span class="ident">is_some</span>());
+    }
+
+    <span class="attribute">#[<span class="ident">apply</span>(<span class="ident">async_fixture_cases</span>)]</span>
+    <span class="kw">fn</span> <span class="ident">fixture_method_should_use_await_if_fixture_function_is_async</span>(
+        <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">is_async</span>: <span class="ident">bool</span>,
+        <span class="ident">method</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>,
+    ) {
+        <span class="kw">let</span> <span class="ident">prefix</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">is_async</span> { <span class="string">&quot;async&quot;</span> } <span class="kw">else</span> { <span class="string">&quot;&quot;</span> };
+        <span class="kw">let</span> (<span class="kw">_</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="kw-2">&amp;</span><span class="macro">format!</span>(
+            <span class="string">r#&quot;
+                    pub {} fn test(mut s: String, v: &amp;u32, a: &amp;mut [i32]) -&gt; u32
+                    {{ }}
+                    &quot;#</span>,
+            <span class="ident">prefix</span>
+        ));
+
+        <span class="kw">let</span> <span class="ident">body</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="ident">method</span>).<span class="ident">unwrap</span>().<span class="ident">block</span>;
+        <span class="kw">let</span> <span class="ident">last_statment</span> <span class="op">=</span> <span class="ident">body</span>.<span class="ident">stmts</span>.<span class="ident">last</span>().<span class="ident">unwrap</span>();
+        <span class="kw">let</span> <span class="ident">is_await</span> <span class="op">=</span> <span class="kw">match</span> <span class="ident">last_statment</span> {
+            <span class="ident">syn::Stmt::Expr</span>(<span class="ident">syn::Expr::Await</span>(<span class="kw">_</span>)) <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">true</span>,
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+        };
+
+        <span class="macro">assert_eq!</span>(<span class="ident">is_async</span>, <span class="ident">is_await</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">implement_a_default_method_with_input_cleaned_fixture_signature_and_no_args</span>() {
+        <span class="kw">let</span> (<span class="ident">item_fn</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(
+            <span class="string">r#&quot;
+                    pub fn test&lt;R: AsRef&lt;str&gt;, B, F, H: Iterator&lt;Item=u32&gt;&gt;(mut s: String, v: &amp;u32, a: &amp;mut [i32], r: R) -&gt; (H, B, String, &amp;str)
+                        where F: ToString,
+                        B: Borrow&lt;u32&gt;
+
+                    { }
+                    &quot;#</span>,
+        );
+
+        <span class="kw">let</span> <span class="ident">default_decl</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="string">&quot;default&quot;</span>).<span class="ident">unwrap</span>().<span class="ident">sig</span>;
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn default&lt;B, H: Iterator&lt;Item=u32&gt;&gt;() -&gt; (H, B, String, &amp;str)
+                            where B: Borrow&lt;u32&gt;
+                    { }
+                    &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>.<span class="ident">sig</span>.<span class="ident">generics</span>, <span class="ident">default_decl</span>.<span class="ident">generics</span>);
+        <span class="macro">assert_eq!</span>(<span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">output</span>, <span class="ident">default_decl</span>.<span class="ident">output</span>);
+        <span class="macro">assert!</span>(<span class="ident">default_decl</span>.<span class="ident">inputs</span>.<span class="ident">is_empty</span>());
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">use_default_return_type_if_any</span>() {
+        <span class="kw">let</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn test&lt;R: AsRef&lt;str&gt;, B, F, H: Iterator&lt;Item=u32&gt;&gt;() -&gt; (H, B)
+                            where F: ToString,
+                            B: Borrow&lt;u32&gt;
+                    { }
+                    &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">tokens</span> <span class="op">=</span> <span class="ident">render</span>(
+            <span class="ident">item_fn</span>.<span class="ident">clone</span>(),
+            <span class="ident">FixtureInfo</span> {
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::Type</span>(
+                        <span class="ident">parse_str</span>(<span class="string">&quot;default&quot;</span>).<span class="ident">unwrap</span>(),
+                        <span class="ident">parse_str</span>(<span class="string">&quot;(impl Iterator&lt;Item=u32&gt;, B)&quot;</span>).<span class="ident">unwrap</span>(),
+                    )],
+                }
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            },
+        );
+        <span class="kw">let</span> <span class="ident">out</span>: <span class="ident">FixtureOutput</span> <span class="op">=</span> <span class="ident">parse2</span>(<span class="ident">tokens</span>).<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">syn::ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn default&lt;B&gt;() -&gt; (impl Iterator&lt;Item=u32&gt;, B)
+                            where B: Borrow&lt;u32&gt;
+                    { }
+                    &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">default_decl</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="string">&quot;default&quot;</span>).<span class="ident">unwrap</span>().<span class="ident">sig</span>;
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>.<span class="ident">sig</span>, <span class="ident">default_decl</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">implement_partial_methods</span>() {
+        <span class="kw">let</span> (<span class="ident">item_fn</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(
+            <span class="string">r#&quot;
+                    pub fn test(mut s: String, v: &amp;u32, a: &amp;mut [i32]) -&gt; usize
+                    { }
+                    &quot;#</span>,
+        );
+
+        <span class="kw">let</span> <span class="ident">partials</span> <span class="op">=</span> (<span class="number">1</span>..<span class="op">=</span><span class="number">3</span>)
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">n</span><span class="op">|</span> {
+                <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>.<span class="ident">clone</span>(), <span class="macro">format!</span>(<span class="string">&quot;partial_{}&quot;</span>, <span class="ident">n</span>))
+                    .<span class="ident">unwrap</span>()
+                    .<span class="ident">sig</span>
+            })
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+        <span class="comment">// All 3 methods found</span>
+
+        <span class="macro">assert!</span>(<span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="string">&quot;partial_4&quot;</span>).<span class="ident">is_none</span>());
+
+        <span class="kw">let</span> <span class="ident">expected_1</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn partial_1(mut s: String) -&gt; usize
+                    { }
+                    &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected_1</span>.<span class="ident">sig</span>, <span class="ident">partials</span>[<span class="number">0</span>]);
+        <span class="kw">for</span> <span class="ident">p</span> <span class="kw">in</span> <span class="ident">partials</span> {
+            <span class="macro">assert_eq!</span>(<span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">output</span>, <span class="ident">p</span>.<span class="ident">output</span>);
+        }
+    }
+
+    <span class="attribute">#[<span class="ident">rstest</span>]</span>
+    <span class="attribute">#[<span class="ident">case::base</span>(<span class="string">&quot;fn test&lt;S: AsRef&lt;str&gt;, U: AsRef&lt;u32&gt;, F: ToString&gt;(mut s: S, v: U) -&gt; F {}&quot;</span>,
+        <span class="macro">vec!</span>[
+            <span class="string">&quot;fn default&lt;F: ToString&gt;() -&gt; F {}&quot;</span>,
+            <span class="string">&quot;fn partial_1&lt;S: AsRef&lt;str&gt;, F: ToString&gt;(mut s: S) -&gt; F {}&quot;</span>,
+            <span class="string">&quot;fn partial_2&lt;S: AsRef&lt;str&gt;, U: AsRef&lt;u32&gt;, F: ToString&gt;(mut s: S, v: U) -&gt; F {}&quot;</span>,
+        ]</span>
+    )]
+    <span class="attribute">#[<span class="ident">case::associated_type</span>(<span class="string">&quot;fn test&lt;T: IntoIterator&gt;(mut i: T) where T::Item: Copy {}&quot;</span>,
+        <span class="macro">vec!</span>[
+            <span class="string">&quot;fn default() {}&quot;</span>,
+            <span class="string">&quot;fn partial_1&lt;T: IntoIterator&gt;(mut i: T) where T::Item: Copy {}&quot;</span>,
+        ]</span>
+    )]
+    <span class="attribute">#[<span class="ident">case::not_remove_const_generics</span>(<span class="string">&quot;fn test&lt;const N:usize&gt;(v: [u32; N]) -&gt; [i32; N] {}&quot;</span>,
+        <span class="macro">vec!</span>[
+            <span class="string">&quot;fn default&lt;const N:usize&gt;() -&gt; [i32; N] {}&quot;</span>,
+            <span class="string">&quot;fn partial_1&lt;const N:usize&gt;(v: [u32; N]) -&gt; [i32; N] {}&quot;</span>,
+        ]</span>
+    )]
+    <span class="attribute">#[<span class="ident">case::remove_const_generics</span>(<span class="string">&quot;fn test&lt;const N:usize&gt;(a: i32, v: [u32; N]) {}&quot;</span>,
+        <span class="macro">vec!</span>[
+            <span class="string">&quot;fn default() {}&quot;</span>,
+            <span class="string">&quot;fn partial_1(a:i32) {}&quot;</span>,
+            <span class="string">&quot;fn partial_2&lt;const N:usize&gt;(a:i32, v: [u32; N]) {}&quot;</span>,
+        ]</span>
+    )]
+
+    <span class="kw">fn</span> <span class="ident">clean_generics</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">code</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">str</span><span class="op">&gt;</span>) {
+        <span class="kw">let</span> (<span class="ident">item_fn</span>, <span class="ident">out</span>) <span class="op">=</span> <span class="ident">parse_fixture</span>(<span class="ident">code</span>);
+        <span class="kw">let</span> <span class="ident">n_args</span> <span class="op">=</span> <span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>().<span class="ident">count</span>();
+
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">signatures</span> <span class="op">=</span> <span class="macro">vec!</span>[<span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>.<span class="ident">clone</span>(), <span class="string">&quot;default&quot;</span>).<span class="ident">unwrap</span>().<span class="ident">sig</span>];
+        <span class="ident">signatures</span>.<span class="ident">extend</span>((<span class="number">1</span>..<span class="op">=</span><span class="ident">n_args</span>).<span class="ident">map</span>(<span class="op">|</span><span class="ident">n</span><span class="op">|</span> {
+            <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>.<span class="ident">clone</span>(), <span class="macro">format!</span>(<span class="string">&quot;partial_{}&quot;</span>, <span class="ident">n</span>))
+                .<span class="ident">unwrap</span>()
+                .<span class="ident">sig</span>
+        }));
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">expected</span>
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">map</span>(<span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>)
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> <span class="ident">f</span>.<span class="ident">unwrap</span>().<span class="ident">sig</span>)
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">signatures</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">use_partial_return_type_if_any</span>() {
+        <span class="kw">let</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn test&lt;R: AsRef&lt;str&gt;, B, F, H: Iterator&lt;Item=u32&gt;&gt;(h: H, b: B) -&gt; (H, B)
+                            where F: ToString,
+                            B: Borrow&lt;u32&gt;
+                    { }
+                     &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">tokens</span> <span class="op">=</span> <span class="ident">render</span>(
+            <span class="ident">item_fn</span>.<span class="ident">clone</span>(),
+            <span class="ident">FixtureInfo</span> {
+                <span class="ident">attributes</span>: <span class="ident">Attributes</span> {
+                    <span class="ident">attributes</span>: <span class="macro">vec!</span>[<span class="ident">Attribute::Type</span>(
+                        <span class="ident">parse_str</span>(<span class="string">&quot;partial_1&quot;</span>).<span class="ident">unwrap</span>(),
+                        <span class="ident">parse_str</span>(<span class="string">&quot;(H, impl Iterator&lt;Item=u32&gt;)&quot;</span>).<span class="ident">unwrap</span>(),
+                    )],
+                }
+                .<span class="ident">into</span>(),
+                ..<span class="ident">Default::default</span>()
+            },
+        );
+        <span class="kw">let</span> <span class="ident">out</span>: <span class="ident">FixtureOutput</span> <span class="op">=</span> <span class="ident">parse2</span>(<span class="ident">tokens</span>).<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">parse_str</span>::<span class="op">&lt;</span><span class="ident">syn::ItemFn</span><span class="op">&gt;</span>(
+            <span class="string">r#&quot;
+                    pub fn partial_1&lt;H: Iterator&lt;Item=u32&gt;&gt;(h: H) -&gt; (H, impl Iterator&lt;Item=u32&gt;)
+                    { }
+                    &quot;#</span>,
+        )
+        .<span class="ident">unwrap</span>();
+
+        <span class="kw">let</span> <span class="ident">partial</span> <span class="op">=</span> <span class="ident">select_method</span>(<span class="ident">out</span>.<span class="ident">core_impl</span>, <span class="string">&quot;partial_1&quot;</span>).<span class="ident">unwrap</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>.<span class="ident">sig</span>, <span class="ident">partial</span>.<span class="ident">sig</span>);
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/mod.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/mod.rs.html
new file mode 100644
index 0000000..128393a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/mod.rs.html
@@ -0,0 +1,745 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/render/mod.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>mod.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+</pre><pre class="rust">
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">fixture</span>;
+<span class="kw">mod</span> <span class="ident">test</span>;
+<span class="kw">mod</span> <span class="ident">wrapper</span>;
+
+<span class="kw">use</span> <span class="ident">std::collections::HashMap</span>;
+<span class="kw">use</span> <span class="ident">syn::token::Async</span>;
+
+<span class="kw">use</span> <span class="ident">proc_macro2</span>::{<span class="ident">Span</span>, <span class="ident">TokenStream</span>};
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">parse_quote</span>, <span class="ident">Attribute</span>, <span class="ident">Expr</span>, <span class="ident">FnArg</span>, <span class="ident">Ident</span>, <span class="ident">ItemFn</span>, <span class="ident">Path</span>, <span class="ident">ReturnType</span>, <span class="ident">Stmt</span>};
+
+<span class="kw">use</span> <span class="ident">quote</span>::{<span class="ident">format_ident</span>, <span class="ident">quote</span>};
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::utils::attr_ends_with</span>;
+<span class="kw">use</span> <span class="kw">crate</span>::{
+    <span class="ident">parse</span>::{
+        <span class="ident">rstest</span>::{<span class="ident">RsTestAttributes</span>, <span class="ident">RsTestData</span>, <span class="ident">RsTestInfo</span>},
+        <span class="ident">testcase::TestCase</span>,
+        <span class="ident">vlist::ValueList</span>,
+    },
+    <span class="ident">utils::attr_is</span>,
+};
+<span class="kw">use</span> <span class="kw">crate</span>::{
+    <span class="ident">refident::MaybeIdent</span>,
+    <span class="ident">resolver</span>::{<span class="self">self</span>, <span class="ident">Resolver</span>},
+};
+<span class="kw">use</span> <span class="ident">wrapper::WrapByModule</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">use</span> <span class="ident">fixture::render</span> <span class="kw">as</span> <span class="ident">fixture</span>;
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">inject</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">single</span>(<span class="kw-2">mut</span> <span class="ident">test</span>: <span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="ident">RsTestInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">resolver::fixtures::get</span>(<span class="ident">info</span>.<span class="ident">data</span>.<span class="ident">fixtures</span>());
+    <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">std::mem::replace</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">test</span>.<span class="ident">attrs</span>, <span class="ident">Default::default</span>());
+    <span class="kw">let</span> <span class="ident">asyncness</span> <span class="op">=</span> <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">asyncness</span>.<span class="ident">clone</span>();
+    <span class="kw">let</span> <span class="ident">generic_types</span> <span class="op">=</span> <span class="ident">test</span>
+        .<span class="ident">sig</span>
+        .<span class="ident">generics</span>
+        .<span class="ident">type_params</span>()
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">tp</span><span class="op">|</span> <span class="kw-2">&amp;</span><span class="ident">tp</span>.<span class="ident">ident</span>)
+        .<span class="ident">cloned</span>()
+        .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+    <span class="ident">single_test_case</span>(
+        <span class="kw-2">&amp;</span><span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>,
+        <span class="kw-2">&amp;</span><span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>,
+        <span class="kw-2">&amp;</span><span class="ident">args</span>,
+        <span class="kw-2">&amp;</span><span class="ident">attrs</span>,
+        <span class="kw-2">&amp;</span><span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">output</span>,
+        <span class="ident">asyncness</span>,
+        <span class="prelude-val">Some</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>),
+        <span class="ident">resolver</span>,
+        <span class="kw-2">&amp;</span><span class="ident">info</span>.<span class="ident">attributes</span>,
+        <span class="kw-2">&amp;</span><span class="ident">generic_types</span>,
+    )
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">parametrize</span>(<span class="ident">test</span>: <span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="ident">RsTestInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">RsTestInfo</span> { <span class="ident">data</span>, <span class="ident">attributes</span> } <span class="op">=</span> <span class="ident">info</span>;
+    <span class="kw">let</span> <span class="ident">resolver_fixtures</span> <span class="op">=</span> <span class="ident">resolver::fixtures::get</span>(<span class="ident">data</span>.<span class="ident">fixtures</span>());
+
+    <span class="kw">let</span> <span class="ident">rendered_cases</span> <span class="op">=</span> <span class="ident">cases_data</span>(<span class="kw-2">&amp;</span><span class="ident">data</span>, <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>.<span class="ident">span</span>())
+        .<span class="ident">map</span>(<span class="op">|</span>(<span class="ident">name</span>, <span class="ident">attrs</span>, <span class="ident">resolver</span>)<span class="op">|</span> {
+            <span class="ident">TestCaseRender::new</span>(<span class="ident">name</span>, <span class="ident">attrs</span>, (<span class="ident">resolver</span>, <span class="kw-2">&amp;</span><span class="ident">resolver_fixtures</span>))
+        })
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">case</span><span class="op">|</span> <span class="ident">case</span>.<span class="ident">render</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>, <span class="kw-2">&amp;</span><span class="ident">attributes</span>))
+        .<span class="ident">collect</span>();
+
+    <span class="ident">test_group</span>(<span class="ident">test</span>, <span class="ident">rendered_cases</span>)
+}
+
+<span class="kw">impl</span> <span class="ident">ValueList</span> {
+    <span class="kw">fn</span> <span class="ident">render</span>(
+        <span class="kw-2">&amp;</span><span class="self">self</span>,
+        <span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>,
+        <span class="ident">resolver</span>: <span class="kw-2">&amp;</span><span class="kw">dyn</span> <span class="ident">Resolver</span>,
+        <span class="ident">attrs</span>: <span class="kw-2">&amp;</span>[<span class="ident">syn::Attribute</span>],
+        <span class="ident">attributes</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestAttributes</span>,
+    ) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+        <span class="kw">let</span> <span class="ident">span</span> <span class="op">=</span> <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>.<span class="ident">span</span>();
+        <span class="kw">let</span> <span class="ident">test_cases</span> <span class="op">=</span> <span class="self">self</span>
+            .<span class="ident">argument_data</span>(<span class="ident">resolver</span>)
+            .<span class="ident">map</span>(<span class="op">|</span>(<span class="ident">name</span>, <span class="ident">r</span>)<span class="op">|</span> <span class="ident">TestCaseRender::new</span>(<span class="ident">Ident::new</span>(<span class="kw-2">&amp;</span><span class="ident">name</span>, <span class="ident">span</span>), <span class="ident">attrs</span>, <span class="ident">r</span>))
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">test_case</span><span class="op">|</span> <span class="ident">test_case</span>.<span class="ident">render</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>, <span class="kw-2">&amp;</span><span class="ident">attributes</span>));
+
+        <span class="macro">quote!</span> { #(#<span class="ident">test_cases</span>)<span class="op">*</span> }
+    }
+
+    <span class="kw">fn</span> <span class="ident">argument_data</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+        <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="self">self</span>,
+        <span class="ident">resolver</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="kw">dyn</span> <span class="ident">Resolver</span>,
+    ) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> (<span class="ident">String</span>, <span class="ident">Box</span><span class="op">&lt;</span>(<span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="kw">dyn</span> <span class="ident">Resolver</span>, (<span class="ident">String</span>, <span class="ident">Expr</span>))<span class="op">&gt;</span>)<span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span> {
+        <span class="kw">let</span> <span class="ident">max_len</span> <span class="op">=</span> <span class="self">self</span>.<span class="ident">values</span>.<span class="ident">len</span>();
+        <span class="self">self</span>.<span class="ident">values</span>.<span class="ident">iter</span>().<span class="ident">enumerate</span>().<span class="ident">map</span>(<span class="kw">move</span> <span class="op">|</span>(<span class="ident">index</span>, <span class="ident">expr</span>)<span class="op">|</span> {
+            <span class="kw">let</span> <span class="ident">name</span> <span class="op">=</span> <span class="macro">format!</span>(
+                <span class="string">&quot;{}_{:0len$}&quot;</span>,
+                <span class="self">self</span>.<span class="ident">arg</span>,
+                <span class="ident">index</span> <span class="op">+</span> <span class="number">1</span>,
+                <span class="ident">len</span> <span class="op">=</span> <span class="ident">max_len</span>.<span class="ident">display_len</span>()
+            );
+            <span class="kw">let</span> <span class="ident">resolver_this</span> <span class="op">=</span> (<span class="self">self</span>.<span class="ident">arg</span>.<span class="ident">to_string</span>(), <span class="ident">expr</span>.<span class="ident">clone</span>());
+            (<span class="ident">name</span>, <span class="ident">Box::new</span>((<span class="ident">resolver</span>, <span class="ident">resolver_this</span>)))
+        })
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">_matrix_recursive</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>,
+    <span class="ident">list_values</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> [<span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">ValueList</span>],
+    <span class="ident">resolver</span>: <span class="kw-2">&amp;</span><span class="kw">dyn</span> <span class="ident">Resolver</span>,
+    <span class="ident">attrs</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> [<span class="ident">syn::Attribute</span>],
+    <span class="ident">attributes</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestAttributes</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">if</span> <span class="ident">list_values</span>.<span class="ident">len</span>() <span class="op">=</span><span class="op">=</span> <span class="number">0</span> {
+        <span class="kw">return</span> <span class="ident">Default::default</span>();
+    }
+    <span class="kw">let</span> <span class="ident">vlist</span> <span class="op">=</span> <span class="ident">list_values</span>[<span class="number">0</span>];
+    <span class="kw">let</span> <span class="ident">list_values</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">list_values</span>[<span class="number">1</span>..];
+
+    <span class="kw">if</span> <span class="ident">list_values</span>.<span class="ident">len</span>() <span class="op">=</span><span class="op">=</span> <span class="number">0</span> {
+        <span class="ident">vlist</span>.<span class="ident">render</span>(<span class="ident">test</span>, <span class="ident">resolver</span>, <span class="ident">attrs</span>, <span class="ident">attributes</span>)
+    } <span class="kw">else</span> {
+        <span class="kw">let</span> <span class="ident">span</span> <span class="op">=</span> <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>.<span class="ident">span</span>();
+        <span class="kw">let</span> <span class="ident">modules</span> <span class="op">=</span> <span class="ident">vlist</span>.<span class="ident">argument_data</span>(<span class="ident">resolver</span>).<span class="ident">map</span>(<span class="kw">move</span> <span class="op">|</span>(<span class="ident">name</span>, <span class="ident">resolver</span>)<span class="op">|</span> {
+            <span class="ident">_matrix_recursive</span>(<span class="ident">test</span>, <span class="ident">list_values</span>, <span class="kw-2">&amp;</span><span class="ident">resolver</span>, <span class="ident">attrs</span>, <span class="ident">attributes</span>)
+                .<span class="ident">wrap_by_mod</span>(<span class="kw-2">&amp;</span><span class="ident">Ident::new</span>(<span class="kw-2">&amp;</span><span class="ident">name</span>, <span class="ident">span</span>))
+        });
+
+        <span class="macro">quote!</span> { #(#<span class="ident">modules</span>)<span class="op">*</span> }
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">matrix</span>(<span class="ident">test</span>: <span class="ident">ItemFn</span>, <span class="ident">info</span>: <span class="ident">RsTestInfo</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">RsTestInfo</span> {
+        <span class="ident">data</span>, <span class="ident">attributes</span>, ..
+    } <span class="op">=</span> <span class="ident">info</span>;
+    <span class="kw">let</span> <span class="ident">span</span> <span class="op">=</span> <span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>.<span class="ident">span</span>();
+
+    <span class="kw">let</span> <span class="ident">cases</span> <span class="op">=</span> <span class="ident">cases_data</span>(<span class="kw-2">&amp;</span><span class="ident">data</span>, <span class="ident">span</span>).<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+    <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">resolver::fixtures::get</span>(<span class="ident">data</span>.<span class="ident">fixtures</span>());
+    <span class="kw">let</span> <span class="ident">rendered_cases</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">cases</span>.<span class="ident">is_empty</span>() {
+        <span class="kw">let</span> <span class="ident">list_values</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">list_values</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+        <span class="ident">_matrix_recursive</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>, <span class="kw-2">&amp;</span><span class="ident">list_values</span>, <span class="kw-2">&amp;</span><span class="ident">resolver</span>, <span class="kw-2">&amp;</span>[], <span class="kw-2">&amp;</span><span class="ident">attributes</span>)
+    } <span class="kw">else</span> {
+        <span class="ident">cases</span>
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">map</span>(<span class="op">|</span>(<span class="ident">case_name</span>, <span class="ident">attrs</span>, <span class="ident">case_resolver</span>)<span class="op">|</span> {
+                <span class="kw">let</span> <span class="ident">list_values</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">list_values</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+                <span class="ident">_matrix_recursive</span>(
+                    <span class="kw-2">&amp;</span><span class="ident">test</span>,
+                    <span class="kw-2">&amp;</span><span class="ident">list_values</span>,
+                    <span class="kw-2">&amp;</span>(<span class="ident">case_resolver</span>, <span class="kw-2">&amp;</span><span class="ident">resolver</span>),
+                    <span class="ident">attrs</span>,
+                    <span class="kw-2">&amp;</span><span class="ident">attributes</span>,
+                )
+                .<span class="ident">wrap_by_mod</span>(<span class="kw-2">&amp;</span><span class="ident">case_name</span>)
+            })
+            .<span class="ident">collect</span>()
+    };
+
+    <span class="ident">test_group</span>(<span class="ident">test</span>, <span class="ident">rendered_cases</span>)
+}
+
+<span class="kw">fn</span> <span class="ident">resolve_default_test_attr</span>(<span class="ident">is_async</span>: <span class="ident">bool</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">if</span> <span class="ident">is_async</span> {
+        <span class="macro">quote!</span> { <span class="attribute">#[<span class="ident">async_std::test</span>]</span> }
+    } <span class="kw">else</span> {
+        <span class="macro">quote!</span> { <span class="attribute">#[<span class="ident">test</span>]</span> }
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">render_exec_call</span>(<span class="ident">fn_path</span>: <span class="ident">Path</span>, <span class="ident">args</span>: <span class="kw-2">&amp;</span>[<span class="ident">Ident</span>], <span class="ident">is_async</span>: <span class="ident">bool</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">if</span> <span class="ident">is_async</span> {
+        <span class="macro">quote!</span> {#<span class="ident">fn_path</span>(#(#<span class="ident">args</span>),<span class="kw-2">*</span>).<span class="kw">await</span>}
+    } <span class="kw">else</span> {
+        <span class="macro">quote!</span> {#<span class="ident">fn_path</span>(#(#<span class="ident">args</span>),<span class="kw-2">*</span>)}
+    }
+}
+
+<span class="doccomment">/// Render a single test case:</span>
+<span class="doccomment">///</span>
+<span class="doccomment">/// * `name` - Test case name</span>
+<span class="doccomment">/// * `testfn_name` - The name of test function to call</span>
+<span class="doccomment">/// * `args` - The arguments of the test function</span>
+<span class="doccomment">/// * `attrs` - The expected test attributes</span>
+<span class="doccomment">/// * `output` - The expected test return type  </span>
+<span class="doccomment">/// * `test_impl` - If you want embed test function (should be the one called by `testfn_name`)</span>
+<span class="doccomment">/// * `resolver` - The resolver used to resolve injected values</span>
+<span class="doccomment">/// * `attributes` - Test attributes to select test behaviour</span>
+<span class="doccomment">///</span>
+<span class="kw">fn</span> <span class="ident">single_test_case</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">name</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>,
+    <span class="ident">testfn_name</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>,
+    <span class="ident">args</span>: <span class="kw-2">&amp;</span>[<span class="ident">FnArg</span>],
+    <span class="ident">attrs</span>: <span class="kw-2">&amp;</span>[<span class="ident">Attribute</span>],
+    <span class="ident">output</span>: <span class="kw-2">&amp;</span><span class="ident">ReturnType</span>,
+    <span class="ident">asyncness</span>: <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Async</span><span class="op">&gt;</span>,
+    <span class="ident">test_impl</span>: <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">ItemFn</span><span class="op">&gt;</span>,
+    <span class="ident">resolver</span>: <span class="kw">impl</span> <span class="ident">Resolver</span>,
+    <span class="ident">attributes</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">RsTestAttributes</span>,
+    <span class="ident">generic_types</span>: <span class="kw-2">&amp;</span>[<span class="ident">Ident</span>],
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> (<span class="ident">attrs</span>, <span class="ident">trace_me</span>): (<span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>, <span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span>) <span class="op">=</span>
+        <span class="ident">attrs</span>.<span class="ident">iter</span>().<span class="ident">cloned</span>().<span class="ident">partition</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="op">!</span><span class="ident">attr_is</span>(<span class="ident">a</span>, <span class="string">&quot;trace&quot;</span>));
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">attributes</span> <span class="op">=</span> <span class="ident">attributes</span>.<span class="ident">clone</span>();
+    <span class="kw">if</span> <span class="ident">trace_me</span>.<span class="ident">len</span>() <span class="op">&gt;</span> <span class="number">0</span> {
+        <span class="ident">attributes</span>.<span class="ident">add_trace</span>(<span class="macro">format_ident!</span>(<span class="string">&quot;trace&quot;</span>));
+    }
+    <span class="kw">let</span> <span class="ident">inject</span> <span class="op">=</span> <span class="ident">inject::resolve_aruments</span>(<span class="ident">args</span>.<span class="ident">iter</span>(), <span class="kw-2">&amp;</span><span class="ident">resolver</span>, <span class="ident">generic_types</span>);
+    <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">args</span>
+        .<span class="ident">iter</span>()
+        .<span class="ident">filter_map</span>(<span class="ident">MaybeIdent::maybe_ident</span>)
+        .<span class="ident">cloned</span>()
+        .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+    <span class="kw">let</span> <span class="ident">trace_args</span> <span class="op">=</span> <span class="ident">trace_arguments</span>(<span class="ident">args</span>.<span class="ident">iter</span>(), <span class="kw-2">&amp;</span><span class="ident">attributes</span>);
+
+    <span class="kw">let</span> <span class="ident">is_async</span> <span class="op">=</span> <span class="ident">asyncness</span>.<span class="ident">is_some</span>();
+    <span class="comment">// If no injected attribut provided use the default one</span>
+    <span class="kw">let</span> <span class="ident">test_attr</span> <span class="op">=</span> <span class="kw">if</span> <span class="ident">attrs</span>
+        .<span class="ident">iter</span>()
+        .<span class="ident">any</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">attr_ends_with</span>(<span class="ident">a</span>, <span class="kw-2">&amp;</span><span class="macro">parse_quote!</span> {<span class="ident">test</span>}))
+    {
+        <span class="prelude-val">None</span>
+    } <span class="kw">else</span> {
+        <span class="prelude-val">Some</span>(<span class="ident">resolve_default_test_attr</span>(<span class="ident">is_async</span>))
+    };
+    <span class="kw">let</span> <span class="ident">execute</span> <span class="op">=</span> <span class="ident">render_exec_call</span>(<span class="ident">testfn_name</span>.<span class="ident">clone</span>().<span class="ident">into</span>(), <span class="kw-2">&amp;</span><span class="ident">args</span>, <span class="ident">is_async</span>);
+
+    <span class="macro">quote!</span> {
+        #<span class="ident">test_attr</span>
+        #(#<span class="ident">attrs</span>)<span class="op">*</span>
+        #<span class="ident">asyncness</span> <span class="kw">fn</span> #<span class="ident">name</span>() #<span class="ident">output</span> {
+            #<span class="ident">test_impl</span>
+            #<span class="ident">inject</span>
+            #<span class="ident">trace_args</span>
+            <span class="macro">println!</span>(<span class="string">&quot;{:-^40}&quot;</span>, <span class="string">&quot; TEST START &quot;</span>);
+            #<span class="ident">execute</span>
+        }
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">trace_arguments</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">args</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">Ident</span><span class="op">&gt;</span>,
+    <span class="ident">attributes</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestAttributes</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">TokenStream</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">statements</span> <span class="op">=</span> <span class="ident">args</span>
+        .<span class="ident">filter</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">arg</span><span class="op">|</span> <span class="ident">attributes</span>.<span class="ident">trace_me</span>(<span class="ident">arg</span>))
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">arg</span><span class="op">|</span> {
+            <span class="macro">parse_quote!</span> {
+                <span class="macro">println!</span>(<span class="string">&quot;{} = {:?}&quot;</span>, <span class="macro">stringify!</span>(#<span class="ident">arg</span>), #<span class="ident">arg</span>);
+            }
+        })
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">stmt</span>: <span class="ident">Stmt</span><span class="op">|</span> <span class="ident">stmt</span>)
+        .<span class="ident">peekable</span>();
+    <span class="kw">if</span> <span class="ident">statements</span>.<span class="ident">peek</span>().<span class="ident">is_some</span>() {
+        <span class="prelude-val">Some</span>(<span class="macro">quote!</span> {
+            <span class="macro">println!</span>(<span class="string">&quot;{:-^40}&quot;</span>, <span class="string">&quot; TEST ARGUMENTS &quot;</span>);
+            #(#<span class="ident">statements</span>)<span class="op">*</span>
+        })
+    } <span class="kw">else</span> {
+        <span class="prelude-val">None</span>
+    }
+}
+
+<span class="kw">struct</span> <span class="ident">TestCaseRender</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> {
+    <span class="ident">name</span>: <span class="ident">Ident</span>,
+    <span class="ident">attrs</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> [<span class="ident">syn::Attribute</span>],
+    <span class="ident">resolver</span>: <span class="ident">Box</span><span class="op">&lt;</span><span class="kw">dyn</span> <span class="ident">Resolver</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span>,
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> <span class="ident">TestCaseRender</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> {
+    <span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">new</span><span class="op">&lt;</span><span class="ident">R</span>: <span class="ident">Resolver</span> <span class="op">+</span> <span class="lifetime">&#39;a</span><span class="op">&gt;</span>(<span class="ident">name</span>: <span class="ident">Ident</span>, <span class="ident">attrs</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> [<span class="ident">syn::Attribute</span>], <span class="ident">resolver</span>: <span class="ident">R</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="ident">TestCaseRender</span> {
+            <span class="ident">name</span>,
+            <span class="ident">attrs</span>,
+            <span class="ident">resolver</span>: <span class="ident">Box::new</span>(<span class="ident">resolver</span>),
+        }
+    }
+
+    <span class="kw">fn</span> <span class="ident">render</span>(<span class="self">self</span>, <span class="ident">testfn</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>, <span class="ident">attributes</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestAttributes</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+        <span class="kw">let</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">testfn</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>().<span class="ident">cloned</span>().<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">attrs</span> <span class="op">=</span> <span class="ident">testfn</span>.<span class="ident">attrs</span>.<span class="ident">clone</span>();
+        <span class="ident">attrs</span>.<span class="ident">extend</span>(<span class="self">self</span>.<span class="ident">attrs</span>.<span class="ident">iter</span>().<span class="ident">cloned</span>());
+        <span class="kw">let</span> <span class="ident">asyncness</span> <span class="op">=</span> <span class="ident">testfn</span>.<span class="ident">sig</span>.<span class="ident">asyncness</span>.<span class="ident">clone</span>();
+        <span class="kw">let</span> <span class="ident">generic_types</span> <span class="op">=</span> <span class="ident">testfn</span>
+            .<span class="ident">sig</span>
+            .<span class="ident">generics</span>
+            .<span class="ident">type_params</span>()
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">tp</span><span class="op">|</span> <span class="kw-2">&amp;</span><span class="ident">tp</span>.<span class="ident">ident</span>)
+            .<span class="ident">cloned</span>()
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">Vec</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+        <span class="ident">single_test_case</span>(
+            <span class="kw-2">&amp;</span><span class="self">self</span>.<span class="ident">name</span>,
+            <span class="kw-2">&amp;</span><span class="ident">testfn</span>.<span class="ident">sig</span>.<span class="ident">ident</span>,
+            <span class="kw-2">&amp;</span><span class="ident">args</span>,
+            <span class="kw-2">&amp;</span><span class="ident">attrs</span>,
+            <span class="kw-2">&amp;</span><span class="ident">testfn</span>.<span class="ident">sig</span>.<span class="ident">output</span>,
+            <span class="ident">asyncness</span>,
+            <span class="prelude-val">None</span>,
+            <span class="self">self</span>.<span class="ident">resolver</span>,
+            <span class="kw-2">&amp;</span><span class="ident">attributes</span>,
+            <span class="kw-2">&amp;</span><span class="ident">generic_types</span>,
+        )
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">test_group</span>(<span class="kw-2">mut</span> <span class="ident">test</span>: <span class="ident">ItemFn</span>, <span class="ident">rendered_cases</span>: <span class="ident">TokenStream</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+    <span class="kw">let</span> <span class="ident">fname</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">test</span>.<span class="ident">sig</span>.<span class="ident">ident</span>;
+    <span class="ident">test</span>.<span class="ident">attrs</span> <span class="op">=</span> <span class="macro">vec!</span>[];
+
+    <span class="macro">quote!</span> {
+        <span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+        #<span class="ident">test</span>
+
+        <span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+        <span class="kw">mod</span> #<span class="ident">fname</span> {
+            <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+            #<span class="ident">rendered_cases</span>
+        }
+    }
+}
+
+<span class="kw">trait</span> <span class="ident">DisplayLen</span> {
+    <span class="kw">fn</span> <span class="ident">display_len</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">usize</span>;
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">D</span>: <span class="ident">std::fmt::Display</span><span class="op">&gt;</span> <span class="ident">DisplayLen</span> <span class="kw">for</span> <span class="ident">D</span> {
+    <span class="kw">fn</span> <span class="ident">display_len</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">usize</span> {
+        <span class="macro">format!</span>(<span class="string">&quot;{}&quot;</span>, <span class="self">self</span>).<span class="ident">len</span>()
+    }
+}
+
+<span class="kw">fn</span> <span class="ident">format_case_name</span>(<span class="ident">case</span>: <span class="kw-2">&amp;</span><span class="ident">TestCase</span>, <span class="ident">index</span>: <span class="ident">usize</span>, <span class="ident">display_len</span>: <span class="ident">usize</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">String</span> {
+    <span class="kw">let</span> <span class="ident">description</span> <span class="op">=</span> <span class="ident">case</span>
+        .<span class="ident">description</span>
+        .<span class="ident">as_ref</span>()
+        .<span class="ident">map</span>(<span class="op">|</span><span class="ident">d</span><span class="op">|</span> <span class="macro">format!</span>(<span class="string">&quot;_{}&quot;</span>, <span class="ident">d</span>))
+        .<span class="ident">unwrap_or_default</span>();
+    <span class="macro">format!</span>(
+        <span class="string">&quot;case_{:0len$}{d}&quot;</span>,
+        <span class="ident">index</span>,
+        <span class="ident">len</span> <span class="op">=</span> <span class="ident">display_len</span>,
+        <span class="ident">d</span> <span class="op">=</span> <span class="ident">description</span>
+    )
+}
+
+<span class="kw">fn</span> <span class="ident">cases_data</span>(
+    <span class="ident">data</span>: <span class="kw-2">&amp;</span><span class="ident">RsTestData</span>,
+    <span class="ident">name_span</span>: <span class="ident">Span</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> (<span class="ident">Ident</span>, <span class="kw-2">&amp;</span>[<span class="ident">syn::Attribute</span>], <span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">String</span>, <span class="kw-2">&amp;</span><span class="ident">syn::Expr</span><span class="op">&gt;</span>)<span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="ident">display_len</span> <span class="op">=</span> <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">count</span>().<span class="ident">display_len</span>();
+    <span class="ident">data</span>.<span class="ident">cases</span>().<span class="ident">enumerate</span>().<span class="ident">map</span>({
+        <span class="kw">move</span> <span class="op">|</span>(<span class="ident">n</span>, <span class="ident">case</span>)<span class="op">|</span> {
+            <span class="kw">let</span> <span class="ident">resolver_case</span> <span class="op">=</span> <span class="ident">data</span>
+                .<span class="ident">case_args</span>()
+                .<span class="ident">map</span>(<span class="op">|</span><span class="ident">a</span><span class="op">|</span> <span class="ident">a</span>.<span class="ident">to_string</span>())
+                .<span class="ident">zip</span>(<span class="ident">case</span>.<span class="ident">args</span>.<span class="ident">iter</span>())
+                .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">HashMap</span><span class="op">&lt;</span><span class="kw">_</span>, <span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+            (
+                <span class="ident">Ident::new</span>(<span class="kw-2">&amp;</span><span class="ident">format_case_name</span>(<span class="ident">case</span>, <span class="ident">n</span> <span class="op">+</span> <span class="number">1</span>, <span class="ident">display_len</span>), <span class="ident">name_span</span>),
+                <span class="ident">case</span>.<span class="ident">attrs</span>.<span class="ident">as_slice</span>(),
+                <span class="ident">resolver_case</span>,
+            )
+        }
+    })
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/wrapper.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/wrapper.rs.html
new file mode 100644
index 0000000..58aaaf3
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/render/wrapper.rs.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/render/wrapper.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>wrapper.rs - source</title><link rel="stylesheet" type="text/css" href="../../../normalize.css"><link rel="stylesheet" type="text/css" href="../../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../../ayu.css" disabled ><script id="default-settings"></script><script src="../../../storage.js"></script><script src="../../../crates.js"></script><noscript><link rel="stylesheet" href="../../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../../settings.html"><img src="../../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1"> 1</span>
+<span id="2"> 2</span>
+<span id="3"> 3</span>
+<span id="4"> 4</span>
+<span id="5"> 5</span>
+<span id="6"> 6</span>
+<span id="7"> 7</span>
+<span id="8"> 8</span>
+<span id="9"> 9</span>
+<span id="10">10</span>
+<span id="11">11</span>
+<span id="12">12</span>
+<span id="13">13</span>
+<span id="14">14</span>
+<span id="15">15</span>
+<span id="16">16</span>
+<span id="17">17</span>
+<span id="18">18</span>
+<span id="19">19</span>
+</pre><pre class="rust">
+<span class="kw">use</span> <span class="ident">proc_macro2::TokenStream</span>;
+<span class="kw">use</span> <span class="ident">quote</span>::{<span class="ident">quote</span>, <span class="ident">ToTokens</span>};
+<span class="kw">use</span> <span class="ident">syn::Ident</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">trait</span> <span class="ident">WrapByModule</span> {
+    <span class="kw">fn</span> <span class="ident">wrap_by_mod</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">mod_name</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span>;
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">T</span>: <span class="ident">ToTokens</span><span class="op">&gt;</span> <span class="ident">WrapByModule</span> <span class="kw">for</span> <span class="ident">T</span> {
+    <span class="kw">fn</span> <span class="ident">wrap_by_mod</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">mod_name</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">TokenStream</span> {
+        <span class="macro">quote!</span> {
+            <span class="kw">mod</span> #<span class="ident">mod_name</span> {
+                <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+                #<span class="self">self</span>
+            }
+        }
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../../" data-current-crate="rstest" data-search-index-js="../../../search-index.js" data-search-js="../../../search.js"></div>
+    <script src="../../../main.js"></script><script src="../../../source-script.js"></script><script src="../../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/resolver.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/resolver.rs.html
new file mode 100644
index 0000000..ca320a3
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/resolver.rs.html
@@ -0,0 +1,355 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/resolver.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>resolver.rs - source</title><link rel="stylesheet" type="text/css" href="../../normalize.css"><link rel="stylesheet" type="text/css" href="../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../ayu.css" disabled ><script id="default-settings"></script><script src="../../storage.js"></script><script src="../../crates.js"></script><noscript><link rel="stylesheet" href="../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../settings.html"><img src="../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+</pre><pre class="rust">
+<span class="doccomment">/// Define `Resolver` trait and implement it on some hashmaps and also define the `Resolver` tuple</span>
+<span class="doccomment">/// composition. Provide also some utility functions related to how to create a `Resolver` and</span>
+<span class="doccomment">/// resolving render.</span>
+<span class="doccomment">///</span>
+<span class="kw">use</span> <span class="ident">std::borrow::Cow</span>;
+<span class="kw">use</span> <span class="ident">std::collections::HashMap</span>;
+
+<span class="kw">use</span> <span class="ident">proc_macro2::Ident</span>;
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">parse_quote</span>, <span class="ident">Expr</span>};
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse::Fixture</span>;
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">fixtures</span> {
+    <span class="kw">use</span> <span class="ident">quote::format_ident</span>;
+
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">get</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(<span class="ident">fixtures</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">Fixture</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Resolver</span> <span class="op">+</span> <span class="lifetime">&#39;a</span> {
+        <span class="ident">fixtures</span>
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">f</span><span class="op">|</span> (<span class="ident">f</span>.<span class="ident">name</span>.<span class="ident">to_string</span>(), <span class="ident">extract_resolve_expression</span>(<span class="ident">f</span>).<span class="ident">into</span>()))
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">HashMap</span><span class="op">&lt;</span><span class="kw">_</span>, <span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span>()
+    }
+
+    <span class="kw">fn</span> <span class="ident">extract_resolve_expression</span>(<span class="ident">fixture</span>: <span class="kw-2">&amp;</span><span class="ident">Fixture</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Expr</span> {
+        <span class="kw">let</span> <span class="ident">resolve</span> <span class="op">=</span> <span class="ident">fixture</span>.<span class="ident">resolve</span>.<span class="ident">as_ref</span>().<span class="ident">unwrap_or</span>(<span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">name</span>);
+        <span class="kw">let</span> <span class="ident">positional</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">fixture</span>.<span class="ident">positional</span>.<span class="number">0</span>;
+        <span class="kw">let</span> <span class="ident">f_name</span> <span class="op">=</span> <span class="kw">match</span> <span class="ident">positional</span>.<span class="ident">len</span>() {
+            <span class="number">0</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">format_ident!</span>(<span class="string">&quot;default&quot;</span>),
+            <span class="ident">l</span> <span class="op">=</span><span class="op">&gt;</span> <span class="macro">format_ident!</span>(<span class="string">&quot;partial_{}&quot;</span>, <span class="ident">l</span>),
+        };
+        <span class="macro">parse_quote!</span> { #<span class="ident">resolve</span>::#<span class="ident">f_name</span>(#(#<span class="ident">positional</span>), <span class="kw-2">*</span>) }
+    }
+
+    <span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+    <span class="kw">mod</span> <span class="ident">should</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+        <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+        <span class="attribute">#[<span class="ident">rstest</span>]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[]</span>, <span class="string">&quot;default()&quot;</span>)]
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[<span class="string">&quot;my_expression&quot;</span>]</span>, <span class="string">&quot;partial_1(my_expression)&quot;</span>)]
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[<span class="string">&quot;first&quot;</span>, <span class="string">&quot;other&quot;</span>]</span>, <span class="string">&quot;partial_2(first, other)&quot;</span>)]
+        <span class="kw">fn</span> <span class="ident">resolve_by_use_the_given_name</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">args</span>: <span class="kw-2">&amp;</span>[<span class="kw-2">&amp;</span><span class="ident">str</span>], <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;pippo&quot;</span>, <span class="ident">args</span>)];
+            <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">get</span>(<span class="ident">data</span>.<span class="ident">iter</span>());
+
+            <span class="kw">let</span> <span class="ident">resolved</span> <span class="op">=</span> <span class="ident">resolver</span>.<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;pippo&quot;</span>)).<span class="ident">unwrap</span>().<span class="ident">into_owned</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="ident">resolved</span>, <span class="macro">format!</span>(<span class="string">&quot;pippo::{}&quot;</span>, <span class="ident">expected</span>).<span class="ident">ast</span>());
+        }
+
+        <span class="attribute">#[<span class="ident">rstest</span>]</span>
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[]</span>, <span class="string">&quot;default()&quot;</span>)]
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[<span class="string">&quot;my_expression&quot;</span>]</span>, <span class="string">&quot;partial_1(my_expression)&quot;</span>)]
+        <span class="attribute">#[<span class="ident">case</span>(<span class="kw-2">&amp;</span>[<span class="string">&quot;first&quot;</span>, <span class="string">&quot;other&quot;</span>]</span>, <span class="string">&quot;partial_2(first, other)&quot;</span>)]
+        <span class="kw">fn</span> <span class="ident">resolve_by_use_the_resolve_field</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">args</span>: <span class="kw-2">&amp;</span>[<span class="kw-2">&amp;</span><span class="ident">str</span>], <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="macro">vec!</span>[<span class="ident">fixture</span>(<span class="string">&quot;pippo&quot;</span>, <span class="ident">args</span>).<span class="ident">with_resolve</span>(<span class="string">&quot;pluto&quot;</span>)];
+            <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">get</span>(<span class="ident">data</span>.<span class="ident">iter</span>());
+
+            <span class="kw">let</span> <span class="ident">resolved</span> <span class="op">=</span> <span class="ident">resolver</span>.<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;pippo&quot;</span>)).<span class="ident">unwrap</span>().<span class="ident">into_owned</span>();
+
+            <span class="macro">assert_eq!</span>(<span class="ident">resolved</span>, <span class="macro">format!</span>(<span class="string">&quot;pluto::{}&quot;</span>, <span class="ident">expected</span>).<span class="ident">ast</span>());
+        }
+    }
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">mod</span> <span class="ident">values</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::parse::fixture::ArgumentValue</span>;
+
+    <span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">get</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(<span class="ident">values</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">ArgumentValue</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Resolver</span> <span class="op">+</span> <span class="lifetime">&#39;a</span> {
+        <span class="ident">values</span>
+            .<span class="ident">map</span>(<span class="op">|</span><span class="ident">av</span><span class="op">|</span> (<span class="ident">av</span>.<span class="ident">name</span>.<span class="ident">to_string</span>(), <span class="kw-2">&amp;</span><span class="ident">av</span>.<span class="ident">expr</span>))
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">HashMap</span><span class="op">&lt;</span><span class="kw">_</span>, <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span>()
+    }
+
+    <span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+    <span class="kw">mod</span> <span class="ident">should</span> {
+        <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+        <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+
+        <span class="attribute">#[<span class="ident">test</span>]</span>
+        <span class="kw">fn</span> <span class="ident">resolve_by_use_the_given_name</span>() {
+            <span class="kw">let</span> <span class="ident">data</span> <span class="op">=</span> <span class="macro">vec!</span>[
+                <span class="ident">arg_value</span>(<span class="string">&quot;pippo&quot;</span>, <span class="string">&quot;42&quot;</span>),
+                <span class="ident">arg_value</span>(<span class="string">&quot;donaldduck&quot;</span>, <span class="string">&quot;vec![1,2]&quot;</span>),
+            ];
+            <span class="kw">let</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">get</span>(<span class="ident">data</span>.<span class="ident">iter</span>());
+
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">resolver</span>.<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;pippo&quot;</span>)).<span class="ident">unwrap</span>().<span class="ident">into_owned</span>(),
+                <span class="string">&quot;42&quot;</span>.<span class="ident">ast</span>()
+            );
+            <span class="macro">assert_eq!</span>(
+                <span class="ident">resolver</span>.<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;donaldduck&quot;</span>)).<span class="ident">unwrap</span>().<span class="ident">into_owned</span>(),
+                <span class="string">&quot;vec![1,2]&quot;</span>.<span class="ident">ast</span>()
+            );
+        }
+    }
+}
+
+<span class="doccomment">/// A trait that `resolve` the given ident to expression code to assign the value.</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">trait</span> <span class="ident">Resolver</span> {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span>;
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> <span class="ident">Resolver</span> <span class="kw">for</span> <span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">String</span>, <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">Expr</span><span class="op">&gt;</span> {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">ident</span> <span class="op">=</span> <span class="ident">ident</span>.<span class="ident">to_string</span>();
+        <span class="self">self</span>.<span class="ident">get</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>).<span class="ident">map</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">c</span><span class="op">|</span> <span class="ident">Cow::Borrowed</span>(<span class="ident">c</span>))
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span> <span class="ident">Resolver</span> <span class="kw">for</span> <span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">String</span>, <span class="ident">Expr</span><span class="op">&gt;</span> {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        <span class="kw">let</span> <span class="ident">ident</span> <span class="op">=</span> <span class="ident">ident</span>.<span class="ident">to_string</span>();
+        <span class="self">self</span>.<span class="ident">get</span>(<span class="kw-2">&amp;</span><span class="ident">ident</span>).<span class="ident">map</span>(<span class="op">|</span><span class="ident">c</span><span class="op">|</span> <span class="ident">Cow::Borrowed</span>(<span class="ident">c</span>))
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">R1</span>: <span class="ident">Resolver</span>, <span class="ident">R2</span>: <span class="ident">Resolver</span><span class="op">&gt;</span> <span class="ident">Resolver</span> <span class="kw">for</span> (<span class="ident">R1</span>, <span class="ident">R2</span>) {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>.<span class="ident">resolve</span>(<span class="ident">ident</span>).<span class="ident">or_else</span>(<span class="op">|</span><span class="op">|</span> <span class="self">self</span>.<span class="number">1</span>.<span class="ident">resolve</span>(<span class="ident">ident</span>))
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">R</span>: <span class="ident">Resolver</span> <span class="op">+</span> <span class="question-mark">?</span><span class="ident">Sized</span><span class="op">&gt;</span> <span class="ident">Resolver</span> <span class="kw">for</span> <span class="kw-2">&amp;</span><span class="ident">R</span> {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        (<span class="kw-2">*</span><span class="self">self</span>).<span class="ident">resolve</span>(<span class="ident">ident</span>)
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">R</span>: <span class="ident">Resolver</span> <span class="op">+</span> <span class="question-mark">?</span><span class="ident">Sized</span><span class="op">&gt;</span> <span class="ident">Resolver</span> <span class="kw">for</span> <span class="ident">Box</span><span class="op">&lt;</span><span class="ident">R</span><span class="op">&gt;</span> {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        (<span class="kw-2">*</span><span class="kw-2">*</span><span class="self">self</span>).<span class="ident">resolve</span>(<span class="ident">ident</span>)
+    }
+}
+
+<span class="kw">impl</span> <span class="ident">Resolver</span> <span class="kw">for</span> (<span class="ident">String</span>, <span class="ident">Expr</span>) {
+    <span class="kw">fn</span> <span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="self">self</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="ident">Cow</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+        <span class="kw">if</span> <span class="self">self</span>.<span class="number">0</span> <span class="op">=</span><span class="op">=</span> <span class="ident">ident</span>.<span class="ident">to_string</span>() {
+            <span class="prelude-val">Some</span>(<span class="ident">Cow::Borrowed</span>(<span class="kw-2">&amp;</span><span class="self">self</span>.<span class="number">1</span>))
+        } <span class="kw">else</span> {
+            <span class="prelude-val">None</span>
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">should</span> {
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+    <span class="kw">use</span> <span class="ident">syn::parse_str</span>;
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">return_the_given_expression</span>() {
+        <span class="kw">let</span> <span class="ident">ast</span> <span class="op">=</span> <span class="ident">parse_str</span>(<span class="string">&quot;fn function(mut foo: String) {}&quot;</span>).<span class="ident">unwrap</span>();
+        <span class="kw">let</span> <span class="ident">arg</span> <span class="op">=</span> <span class="ident">first_arg_ident</span>(<span class="kw-2">&amp;</span><span class="ident">ast</span>);
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="ident">expr</span>(<span class="string">&quot;bar()&quot;</span>);
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">resolver</span> <span class="op">=</span> <span class="ident">HashMap::new</span>();
+
+        <span class="ident">resolver</span>.<span class="ident">insert</span>(<span class="string">&quot;foo&quot;</span>.<span class="ident">to_string</span>(), <span class="kw-2">&amp;</span><span class="ident">expected</span>);
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, (<span class="kw-2">&amp;</span><span class="ident">resolver</span>).<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">arg</span>).<span class="ident">unwrap</span>().<span class="ident">into_owned</span>())
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">return_none_for_unknown_argument</span>() {
+        <span class="kw">let</span> <span class="ident">ast</span> <span class="op">=</span> <span class="string">&quot;fn function(mut fix: String) {}&quot;</span>.<span class="ident">ast</span>();
+        <span class="kw">let</span> <span class="ident">arg</span> <span class="op">=</span> <span class="ident">first_arg_ident</span>(<span class="kw-2">&amp;</span><span class="ident">ast</span>);
+
+        <span class="macro">assert!</span>(<span class="ident">EmptyResolver</span>.<span class="ident">resolve</span>(<span class="kw-2">&amp;</span><span class="ident">arg</span>).<span class="ident">is_none</span>())
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../" data-current-crate="rstest" data-search-index-js="../../search-index.js" data-search-js="../../search.js"></div>
+    <script src="../../main.js"></script><script src="../../source-script.js"></script><script src="../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/utils.rs.html b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/utils.rs.html
new file mode 100644
index 0000000..900fea1
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/src/rstest/utils.rs.html
@@ -0,0 +1,769 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/utils.rs`."><meta name="keywords" content="rust, rustlang, rust-lang"><title>utils.rs - source</title><link rel="stylesheet" type="text/css" href="../../normalize.css"><link rel="stylesheet" type="text/css" href="../../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../../light.css"  id="themeStyle"><link rel="stylesheet" type="text/css" href="../../dark.css" disabled ><link rel="stylesheet" type="text/css" href="../../ayu.css" disabled ><script id="default-settings"></script><script src="../../storage.js"></script><script src="../../crates.js"></script><noscript><link rel="stylesheet" href="../../noscript.css"></noscript><link rel="icon" type="image/svg+xml" href="../../favicon.svg">
+<link rel="alternate icon" type="image/png" href="../../favicon-16x16.png">
+<link rel="alternate icon" type="image/png" href="../../favicon-32x32.png"><style type="text/css">#crate-search{background-image:url("../../down-arrow.svg");}</style></head><body class="rustdoc source"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="sidebar-menu" role="button">&#9776;</div><a href='../../rstest/index.html'><div class='logo-container rust-logo'><img src='../../rust-logo.png' alt='logo'></div></a></nav><div class="theme-picker"><button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu"><img src="../../brush.svg" width="18" height="18" alt="Pick another theme!"></button><div id="theme-choices" role="menu"></div></div><nav class="sub"><form class="search-form"><div class="search-container"><div><select id="crate-search"><option value="All crates">All crates</option></select><input class="search-input" name="search" disabled autocomplete="off" spellcheck="false" placeholder="Click or press ‘S’ to search, ‘?’ for more options…" type="search"></div><button type="button" id="help-button">?</button>
+                <a id="settings-menu" href="../../settings.html"><img src="../../wheel.svg" width="18" height="18" alt="Change settings"></a></div></form></nav><section id="main" class="content"><div class="example-wrap"><pre class="line-numbers"><span id="1">  1</span>
+<span id="2">  2</span>
+<span id="3">  3</span>
+<span id="4">  4</span>
+<span id="5">  5</span>
+<span id="6">  6</span>
+<span id="7">  7</span>
+<span id="8">  8</span>
+<span id="9">  9</span>
+<span id="10"> 10</span>
+<span id="11"> 11</span>
+<span id="12"> 12</span>
+<span id="13"> 13</span>
+<span id="14"> 14</span>
+<span id="15"> 15</span>
+<span id="16"> 16</span>
+<span id="17"> 17</span>
+<span id="18"> 18</span>
+<span id="19"> 19</span>
+<span id="20"> 20</span>
+<span id="21"> 21</span>
+<span id="22"> 22</span>
+<span id="23"> 23</span>
+<span id="24"> 24</span>
+<span id="25"> 25</span>
+<span id="26"> 26</span>
+<span id="27"> 27</span>
+<span id="28"> 28</span>
+<span id="29"> 29</span>
+<span id="30"> 30</span>
+<span id="31"> 31</span>
+<span id="32"> 32</span>
+<span id="33"> 33</span>
+<span id="34"> 34</span>
+<span id="35"> 35</span>
+<span id="36"> 36</span>
+<span id="37"> 37</span>
+<span id="38"> 38</span>
+<span id="39"> 39</span>
+<span id="40"> 40</span>
+<span id="41"> 41</span>
+<span id="42"> 42</span>
+<span id="43"> 43</span>
+<span id="44"> 44</span>
+<span id="45"> 45</span>
+<span id="46"> 46</span>
+<span id="47"> 47</span>
+<span id="48"> 48</span>
+<span id="49"> 49</span>
+<span id="50"> 50</span>
+<span id="51"> 51</span>
+<span id="52"> 52</span>
+<span id="53"> 53</span>
+<span id="54"> 54</span>
+<span id="55"> 55</span>
+<span id="56"> 56</span>
+<span id="57"> 57</span>
+<span id="58"> 58</span>
+<span id="59"> 59</span>
+<span id="60"> 60</span>
+<span id="61"> 61</span>
+<span id="62"> 62</span>
+<span id="63"> 63</span>
+<span id="64"> 64</span>
+<span id="65"> 65</span>
+<span id="66"> 66</span>
+<span id="67"> 67</span>
+<span id="68"> 68</span>
+<span id="69"> 69</span>
+<span id="70"> 70</span>
+<span id="71"> 71</span>
+<span id="72"> 72</span>
+<span id="73"> 73</span>
+<span id="74"> 74</span>
+<span id="75"> 75</span>
+<span id="76"> 76</span>
+<span id="77"> 77</span>
+<span id="78"> 78</span>
+<span id="79"> 79</span>
+<span id="80"> 80</span>
+<span id="81"> 81</span>
+<span id="82"> 82</span>
+<span id="83"> 83</span>
+<span id="84"> 84</span>
+<span id="85"> 85</span>
+<span id="86"> 86</span>
+<span id="87"> 87</span>
+<span id="88"> 88</span>
+<span id="89"> 89</span>
+<span id="90"> 90</span>
+<span id="91"> 91</span>
+<span id="92"> 92</span>
+<span id="93"> 93</span>
+<span id="94"> 94</span>
+<span id="95"> 95</span>
+<span id="96"> 96</span>
+<span id="97"> 97</span>
+<span id="98"> 98</span>
+<span id="99"> 99</span>
+<span id="100">100</span>
+<span id="101">101</span>
+<span id="102">102</span>
+<span id="103">103</span>
+<span id="104">104</span>
+<span id="105">105</span>
+<span id="106">106</span>
+<span id="107">107</span>
+<span id="108">108</span>
+<span id="109">109</span>
+<span id="110">110</span>
+<span id="111">111</span>
+<span id="112">112</span>
+<span id="113">113</span>
+<span id="114">114</span>
+<span id="115">115</span>
+<span id="116">116</span>
+<span id="117">117</span>
+<span id="118">118</span>
+<span id="119">119</span>
+<span id="120">120</span>
+<span id="121">121</span>
+<span id="122">122</span>
+<span id="123">123</span>
+<span id="124">124</span>
+<span id="125">125</span>
+<span id="126">126</span>
+<span id="127">127</span>
+<span id="128">128</span>
+<span id="129">129</span>
+<span id="130">130</span>
+<span id="131">131</span>
+<span id="132">132</span>
+<span id="133">133</span>
+<span id="134">134</span>
+<span id="135">135</span>
+<span id="136">136</span>
+<span id="137">137</span>
+<span id="138">138</span>
+<span id="139">139</span>
+<span id="140">140</span>
+<span id="141">141</span>
+<span id="142">142</span>
+<span id="143">143</span>
+<span id="144">144</span>
+<span id="145">145</span>
+<span id="146">146</span>
+<span id="147">147</span>
+<span id="148">148</span>
+<span id="149">149</span>
+<span id="150">150</span>
+<span id="151">151</span>
+<span id="152">152</span>
+<span id="153">153</span>
+<span id="154">154</span>
+<span id="155">155</span>
+<span id="156">156</span>
+<span id="157">157</span>
+<span id="158">158</span>
+<span id="159">159</span>
+<span id="160">160</span>
+<span id="161">161</span>
+<span id="162">162</span>
+<span id="163">163</span>
+<span id="164">164</span>
+<span id="165">165</span>
+<span id="166">166</span>
+<span id="167">167</span>
+<span id="168">168</span>
+<span id="169">169</span>
+<span id="170">170</span>
+<span id="171">171</span>
+<span id="172">172</span>
+<span id="173">173</span>
+<span id="174">174</span>
+<span id="175">175</span>
+<span id="176">176</span>
+<span id="177">177</span>
+<span id="178">178</span>
+<span id="179">179</span>
+<span id="180">180</span>
+<span id="181">181</span>
+<span id="182">182</span>
+<span id="183">183</span>
+<span id="184">184</span>
+<span id="185">185</span>
+<span id="186">186</span>
+<span id="187">187</span>
+<span id="188">188</span>
+<span id="189">189</span>
+<span id="190">190</span>
+<span id="191">191</span>
+<span id="192">192</span>
+<span id="193">193</span>
+<span id="194">194</span>
+<span id="195">195</span>
+<span id="196">196</span>
+<span id="197">197</span>
+<span id="198">198</span>
+<span id="199">199</span>
+<span id="200">200</span>
+<span id="201">201</span>
+<span id="202">202</span>
+<span id="203">203</span>
+<span id="204">204</span>
+<span id="205">205</span>
+<span id="206">206</span>
+<span id="207">207</span>
+<span id="208">208</span>
+<span id="209">209</span>
+<span id="210">210</span>
+<span id="211">211</span>
+<span id="212">212</span>
+<span id="213">213</span>
+<span id="214">214</span>
+<span id="215">215</span>
+<span id="216">216</span>
+<span id="217">217</span>
+<span id="218">218</span>
+<span id="219">219</span>
+<span id="220">220</span>
+<span id="221">221</span>
+<span id="222">222</span>
+<span id="223">223</span>
+<span id="224">224</span>
+<span id="225">225</span>
+<span id="226">226</span>
+<span id="227">227</span>
+<span id="228">228</span>
+<span id="229">229</span>
+<span id="230">230</span>
+<span id="231">231</span>
+<span id="232">232</span>
+<span id="233">233</span>
+<span id="234">234</span>
+<span id="235">235</span>
+<span id="236">236</span>
+<span id="237">237</span>
+<span id="238">238</span>
+<span id="239">239</span>
+<span id="240">240</span>
+<span id="241">241</span>
+<span id="242">242</span>
+<span id="243">243</span>
+<span id="244">244</span>
+<span id="245">245</span>
+<span id="246">246</span>
+<span id="247">247</span>
+<span id="248">248</span>
+<span id="249">249</span>
+<span id="250">250</span>
+<span id="251">251</span>
+<span id="252">252</span>
+<span id="253">253</span>
+<span id="254">254</span>
+<span id="255">255</span>
+<span id="256">256</span>
+<span id="257">257</span>
+<span id="258">258</span>
+<span id="259">259</span>
+<span id="260">260</span>
+<span id="261">261</span>
+<span id="262">262</span>
+<span id="263">263</span>
+<span id="264">264</span>
+<span id="265">265</span>
+<span id="266">266</span>
+<span id="267">267</span>
+<span id="268">268</span>
+<span id="269">269</span>
+<span id="270">270</span>
+<span id="271">271</span>
+<span id="272">272</span>
+<span id="273">273</span>
+<span id="274">274</span>
+<span id="275">275</span>
+<span id="276">276</span>
+<span id="277">277</span>
+<span id="278">278</span>
+<span id="279">279</span>
+<span id="280">280</span>
+<span id="281">281</span>
+<span id="282">282</span>
+<span id="283">283</span>
+<span id="284">284</span>
+<span id="285">285</span>
+<span id="286">286</span>
+<span id="287">287</span>
+<span id="288">288</span>
+<span id="289">289</span>
+<span id="290">290</span>
+<span id="291">291</span>
+<span id="292">292</span>
+<span id="293">293</span>
+<span id="294">294</span>
+<span id="295">295</span>
+<span id="296">296</span>
+<span id="297">297</span>
+<span id="298">298</span>
+<span id="299">299</span>
+<span id="300">300</span>
+<span id="301">301</span>
+<span id="302">302</span>
+<span id="303">303</span>
+<span id="304">304</span>
+<span id="305">305</span>
+<span id="306">306</span>
+<span id="307">307</span>
+<span id="308">308</span>
+<span id="309">309</span>
+<span id="310">310</span>
+<span id="311">311</span>
+<span id="312">312</span>
+<span id="313">313</span>
+<span id="314">314</span>
+<span id="315">315</span>
+<span id="316">316</span>
+<span id="317">317</span>
+<span id="318">318</span>
+<span id="319">319</span>
+<span id="320">320</span>
+<span id="321">321</span>
+<span id="322">322</span>
+<span id="323">323</span>
+<span id="324">324</span>
+<span id="325">325</span>
+<span id="326">326</span>
+<span id="327">327</span>
+<span id="328">328</span>
+<span id="329">329</span>
+<span id="330">330</span>
+<span id="331">331</span>
+<span id="332">332</span>
+<span id="333">333</span>
+<span id="334">334</span>
+<span id="335">335</span>
+<span id="336">336</span>
+<span id="337">337</span>
+<span id="338">338</span>
+<span id="339">339</span>
+<span id="340">340</span>
+<span id="341">341</span>
+<span id="342">342</span>
+<span id="343">343</span>
+<span id="344">344</span>
+<span id="345">345</span>
+<span id="346">346</span>
+<span id="347">347</span>
+<span id="348">348</span>
+<span id="349">349</span>
+<span id="350">350</span>
+<span id="351">351</span>
+<span id="352">352</span>
+<span id="353">353</span>
+<span id="354">354</span>
+<span id="355">355</span>
+<span id="356">356</span>
+<span id="357">357</span>
+<span id="358">358</span>
+<span id="359">359</span>
+<span id="360">360</span>
+<span id="361">361</span>
+<span id="362">362</span>
+<span id="363">363</span>
+<span id="364">364</span>
+<span id="365">365</span>
+<span id="366">366</span>
+<span id="367">367</span>
+<span id="368">368</span>
+<span id="369">369</span>
+<span id="370">370</span>
+<span id="371">371</span>
+<span id="372">372</span>
+<span id="373">373</span>
+<span id="374">374</span>
+<span id="375">375</span>
+<span id="376">376</span>
+<span id="377">377</span>
+<span id="378">378</span>
+<span id="379">379</span>
+<span id="380">380</span>
+<span id="381">381</span>
+</pre><pre class="rust">
+<span class="doccomment">/// Contains some unsorted functions used across others modules</span>
+<span class="doccomment">///</span>
+<span class="kw">use</span> <span class="ident">quote::format_ident</span>;
+<span class="kw">use</span> <span class="ident">std::collections</span>::{<span class="ident">HashMap</span>, <span class="ident">HashSet</span>};
+
+<span class="kw">use</span> <span class="kw">crate</span><span class="ident">::refident::MaybeIdent</span>;
+<span class="kw">use</span> <span class="ident">syn</span>::{<span class="ident">Attribute</span>, <span class="ident">Expr</span>, <span class="ident">FnArg</span>, <span class="ident">Generics</span>, <span class="ident">Ident</span>, <span class="ident">ItemFn</span>, <span class="ident">ReturnType</span>, <span class="ident">Type</span>, <span class="ident">WherePredicate</span>};
+
+<span class="doccomment">/// Return an iterator over fn arguments items.</span>
+<span class="doccomment">///</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fn_args_idents</span>(<span class="ident">test</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+    <span class="ident">fn_args</span>(<span class="kw-2">&amp;</span><span class="ident">test</span>).<span class="ident">filter_map</span>(<span class="ident">MaybeIdent::maybe_ident</span>)
+}
+
+<span class="doccomment">/// Return if function declaration has an ident</span>
+<span class="doccomment">///</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fn_args_has_ident</span>(<span class="ident">fn_decl</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>, <span class="ident">ident</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="ident">fn_args_idents</span>(<span class="ident">fn_decl</span>).<span class="ident">find</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">id</span><span class="op">|</span> <span class="ident">id</span> <span class="op">=</span><span class="op">=</span> <span class="ident">ident</span>).<span class="ident">is_some</span>()
+}
+
+<span class="doccomment">/// Return an iterator over fn arguments.</span>
+<span class="doccomment">///</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">fn_args</span>(<span class="ident">item_fn</span>: <span class="kw-2">&amp;</span><span class="ident">ItemFn</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">FnArg</span><span class="op">&gt;</span> {
+    <span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>()
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">attr_ends_with</span>(<span class="ident">attr</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>, <span class="ident">segment</span>: <span class="kw-2">&amp;</span><span class="ident">syn::PathSegment</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="kw-2">&amp;</span><span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">segments</span>.<span class="ident">iter</span>().<span class="ident">last</span>() <span class="op">=</span><span class="op">=</span> <span class="kw-2">&amp;</span><span class="prelude-val">Some</span>(<span class="ident">segment</span>)
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">attr_starts_with</span>(<span class="ident">attr</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>, <span class="ident">segment</span>: <span class="kw-2">&amp;</span><span class="ident">syn::PathSegment</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="kw-2">&amp;</span><span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">segments</span>.<span class="ident">iter</span>().<span class="ident">nth</span>(<span class="number">0</span>) <span class="op">=</span><span class="op">=</span> <span class="kw-2">&amp;</span><span class="prelude-val">Some</span>(<span class="ident">segment</span>)
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">attr_is</span>(<span class="ident">attr</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>, <span class="ident">name</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">is_ident</span>(<span class="kw-2">&amp;</span><span class="macro">format_ident!</span>(<span class="string">&quot;{}&quot;</span>, <span class="ident">name</span>))
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">attr_in</span>(<span class="ident">attr</span>: <span class="kw-2">&amp;</span><span class="ident">Attribute</span>, <span class="ident">names</span>: <span class="kw-2">&amp;</span>[<span class="kw-2">&amp;</span><span class="ident">str</span>]) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="ident">names</span>
+        .<span class="ident">into_iter</span>()
+        .<span class="ident">any</span>(<span class="op">|</span><span class="ident">name</span><span class="op">|</span> <span class="ident">attr</span>.<span class="ident">path</span>.<span class="ident">is_ident</span>(<span class="kw-2">&amp;</span><span class="macro">format_ident!</span>(<span class="string">&quot;{}&quot;</span>, <span class="ident">name</span>)))
+}
+
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">trait</span> <span class="ident">IsLiteralExpression</span> {
+    <span class="kw">fn</span> <span class="ident">is_literal</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span>;
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="ident">E</span>: <span class="ident">AsRef</span><span class="op">&lt;</span><span class="ident">Expr</span><span class="op">&gt;</span><span class="op">&gt;</span> <span class="ident">IsLiteralExpression</span> <span class="kw">for</span> <span class="ident">E</span> {
+    <span class="kw">fn</span> <span class="ident">is_literal</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+        <span class="kw">match</span> <span class="self">self</span>.<span class="ident">as_ref</span>() {
+            <span class="kw-2">&amp;</span><span class="ident">Expr::Lit</span>(<span class="ident">syn::ExprLit</span> { <span class="kw-2">ref</span> <span class="ident">lit</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> <span class="kw">match</span> <span class="ident">lit</span> {
+                <span class="ident">syn::Lit::Str</span>(<span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">true</span>,
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+            },
+            <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+        }
+    }
+}
+
+<span class="comment">// Recoursive search id by reference till find one in ends</span>
+<span class="kw">fn</span> <span class="ident">_is_used</span>(
+    <span class="ident">visited</span>: <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>,
+    <span class="ident">id</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>,
+    <span class="ident">references</span>: <span class="kw-2">&amp;</span><span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">Ident</span>, <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span><span class="op">&gt;</span>,
+    <span class="ident">ends</span>: <span class="kw-2">&amp;</span><span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="kw">if</span> <span class="ident">visited</span>.<span class="ident">contains</span>(<span class="ident">id</span>) {
+        <span class="kw">return</span> <span class="bool-val">false</span>;
+    }
+    <span class="ident">visited</span>.<span class="ident">insert</span>(<span class="ident">id</span>.<span class="ident">clone</span>());
+    <span class="kw">if</span> <span class="ident">ends</span>.<span class="ident">contains</span>(<span class="ident">id</span>) {
+        <span class="kw">return</span> <span class="bool-val">true</span>;
+    }
+    <span class="kw">if</span> <span class="ident">references</span>.<span class="ident">contains_key</span>(<span class="ident">id</span>) {
+        <span class="kw">for</span> <span class="ident">refered</span> <span class="kw">in</span> <span class="ident">references</span>.<span class="ident">get</span>(<span class="ident">id</span>).<span class="ident">unwrap</span>() {
+            <span class="kw">if</span> <span class="ident">_is_used</span>(<span class="ident">visited</span>, <span class="ident">refered</span>, <span class="ident">references</span>, <span class="ident">ends</span>) {
+                <span class="kw">return</span> <span class="bool-val">true</span>;
+            }
+        }
+    }
+    <span class="bool-val">false</span>
+}
+
+<span class="comment">// Recoursive search id by reference till find one in ends</span>
+<span class="kw">fn</span> <span class="ident">is_used</span>(<span class="ident">id</span>: <span class="kw-2">&amp;</span><span class="ident">Ident</span>, <span class="ident">references</span>: <span class="kw-2">&amp;</span><span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">Ident</span>, <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span><span class="op">&gt;</span>, <span class="ident">ends</span>: <span class="kw-2">&amp;</span><span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">bool</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">visited</span> <span class="op">=</span> <span class="ident">Default::default</span>();
+    <span class="ident">_is_used</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">visited</span>, <span class="ident">id</span>, <span class="ident">references</span>, <span class="ident">ends</span>)
+}
+
+<span class="kw">impl</span> <span class="ident">MaybeIdent</span> <span class="kw">for</span> <span class="ident">syn::WherePredicate</span> {
+    <span class="kw">fn</span> <span class="ident">maybe_ident</span>(<span class="kw-2">&amp;</span><span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="kw">match</span> <span class="self">self</span> {
+            <span class="ident">WherePredicate::Type</span>(<span class="ident">syn::PredicateType</span> { <span class="ident">bounded_ty</span>: <span class="ident">t</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> {
+                <span class="ident">first_type_path_segment_ident</span>(<span class="ident">t</span>)
+            }
+            <span class="ident">WherePredicate::Lifetime</span>(<span class="ident">syn::PredicateLifetime</span> { <span class="ident">lifetime</span>, .. }) <span class="op">=</span><span class="op">&gt;</span> {
+                <span class="prelude-val">Some</span>(<span class="kw-2">&amp;</span><span class="ident">lifetime</span>.<span class="ident">ident</span>)
+            }
+            <span class="ident">WherePredicate::Eq</span>(<span class="kw">_</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+        }
+    }
+}
+
+<span class="attribute">#[<span class="ident">derive</span>(<span class="ident">Default</span>)]</span>
+<span class="kw">struct</span> <span class="ident">SearchSimpleTypeName</span>(<span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>);
+
+<span class="kw">impl</span> <span class="ident">SearchSimpleTypeName</span> {
+    <span class="kw">fn</span> <span class="ident">take</span>(<span class="self">self</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+        <span class="self">self</span>.<span class="number">0</span>
+    }
+
+    <span class="kw">fn</span> <span class="ident">visit_inputs</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">inputs</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">FnArg</span><span class="op">&gt;</span>) {
+        <span class="kw">use</span> <span class="ident">syn::visit::Visit</span>;
+        <span class="ident">inputs</span>.<span class="ident">for_each</span>(<span class="op">|</span><span class="ident">fn_arg</span><span class="op">|</span> <span class="self">self</span>.<span class="ident">visit_fn_arg</span>(<span class="ident">fn_arg</span>));
+    }
+    <span class="kw">fn</span> <span class="ident">visit_output</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">output</span>: <span class="kw-2">&amp;</span><span class="ident">ReturnType</span>) {
+        <span class="kw">use</span> <span class="ident">syn::visit::Visit</span>;
+        <span class="self">self</span>.<span class="ident">visit_return_type</span>(<span class="ident">output</span>);
+    }
+
+    <span class="kw">fn</span> <span class="ident">collect_from_type_param</span>(<span class="ident">tp</span>: <span class="kw-2">&amp;</span><span class="ident">syn::TypeParam</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span>: <span class="self">Self</span> <span class="op">=</span> <span class="ident">Default::default</span>();
+        <span class="kw">use</span> <span class="ident">syn::visit::Visit</span>;
+        <span class="ident">s</span>.<span class="ident">visit_type_param</span>(<span class="ident">tp</span>);
+        <span class="ident">s</span>
+    }
+
+    <span class="kw">fn</span> <span class="ident">collect_from_where_predicate</span>(<span class="ident">wp</span>: <span class="kw-2">&amp;</span><span class="ident">syn::WherePredicate</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="self">Self</span> {
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span>: <span class="self">Self</span> <span class="op">=</span> <span class="ident">Default::default</span>();
+        <span class="kw">use</span> <span class="ident">syn::visit::Visit</span>;
+        <span class="ident">s</span>.<span class="ident">visit_where_predicate</span>(<span class="ident">wp</span>);
+        <span class="ident">s</span>
+    }
+}
+
+<span class="kw">impl</span><span class="op">&lt;</span><span class="lifetime">&#39;ast</span><span class="op">&gt;</span> <span class="ident">syn::visit::Visit</span><span class="op">&lt;</span><span class="lifetime">&#39;ast</span><span class="op">&gt;</span> <span class="kw">for</span> <span class="ident">SearchSimpleTypeName</span> {
+    <span class="kw">fn</span> <span class="ident">visit_path</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">p</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;ast</span> <span class="ident">syn::Path</span>) {
+        <span class="kw">if</span> <span class="kw">let</span> <span class="prelude-val">Some</span>(<span class="ident">id</span>) <span class="op">=</span> <span class="ident">p</span>.<span class="ident">get_ident</span>() {
+            <span class="self">self</span>.<span class="number">0</span>.<span class="ident">insert</span>(<span class="ident">id</span>.<span class="ident">clone</span>());
+        }
+        <span class="ident">syn::visit::visit_path</span>(<span class="self">self</span>, <span class="ident">p</span>)
+    }
+
+    <span class="kw">fn</span> <span class="ident">visit_lifetime</span>(<span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="self">self</span>, <span class="ident">i</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;ast</span> <span class="ident">syn::Lifetime</span>) {
+        <span class="self">self</span>.<span class="number">0</span>.<span class="ident">insert</span>(<span class="ident">i</span>.<span class="ident">ident</span>.<span class="ident">clone</span>());
+        <span class="ident">syn::visit::visit_lifetime</span>(<span class="self">self</span>, <span class="ident">i</span>)
+    }
+}
+
+<span class="comment">// Take generics definitions and where clauses and return the</span>
+<span class="comment">// a map from simple types (lifetime names or type with just names)</span>
+<span class="comment">// to a set of all simple types that use it as some costrain.</span>
+<span class="kw">fn</span> <span class="ident">extract_references_map</span>(<span class="ident">generics</span>: <span class="kw-2">&amp;</span><span class="ident">Generics</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">HashMap</span><span class="op">&lt;</span><span class="ident">Ident</span>, <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">references</span> <span class="op">=</span> <span class="ident">HashMap</span>::<span class="op">&lt;</span><span class="ident">Ident</span>, <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span><span class="op">&gt;</span><span class="ident">::default</span>();
+    <span class="comment">// Extracts references from types param</span>
+    <span class="ident">generics</span>.<span class="ident">type_params</span>().<span class="ident">for_each</span>(<span class="op">|</span><span class="ident">tp</span><span class="op">|</span> {
+        <span class="ident">SearchSimpleTypeName::collect_from_type_param</span>(<span class="ident">tp</span>)
+            .<span class="ident">take</span>()
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">for_each</span>(<span class="op">|</span><span class="ident">id</span><span class="op">|</span> {
+                <span class="ident">references</span>.<span class="ident">entry</span>(<span class="ident">id</span>).<span class="ident">or_default</span>().<span class="ident">insert</span>(<span class="ident">tp</span>.<span class="ident">ident</span>.<span class="ident">clone</span>());
+            });
+    });
+    <span class="comment">// Extracts references from where clauses</span>
+    <span class="ident">generics</span>
+        .<span class="ident">where_clause</span>
+        .<span class="ident">iter</span>()
+        .<span class="ident">flat_map</span>(<span class="op">|</span><span class="ident">wc</span><span class="op">|</span> <span class="ident">wc</span>.<span class="ident">predicates</span>.<span class="ident">iter</span>())
+        .<span class="ident">filter_map</span>(<span class="op">|</span><span class="ident">wp</span><span class="op">|</span> <span class="ident">wp</span>.<span class="ident">maybe_ident</span>().<span class="ident">map</span>(<span class="op">|</span><span class="ident">id</span><span class="op">|</span> (<span class="ident">id</span>, <span class="ident">wp</span>)))
+        .<span class="ident">for_each</span>(<span class="op">|</span>(<span class="ident">ref_ident</span>, <span class="ident">wp</span>)<span class="op">|</span> {
+            <span class="ident">SearchSimpleTypeName::collect_from_where_predicate</span>(<span class="ident">wp</span>)
+                .<span class="ident">take</span>()
+                .<span class="ident">into_iter</span>()
+                .<span class="ident">for_each</span>(<span class="op">|</span><span class="ident">id</span><span class="op">|</span> {
+                    <span class="ident">references</span>.<span class="ident">entry</span>(<span class="ident">id</span>).<span class="ident">or_default</span>().<span class="ident">insert</span>(<span class="ident">ref_ident</span>.<span class="ident">clone</span>());
+                });
+        });
+    <span class="ident">references</span>
+}
+
+<span class="comment">// Return a hash set that contains all types and lifetimes referenced</span>
+<span class="comment">// in input/output expressed by a single ident.</span>
+<span class="kw">fn</span> <span class="ident">references_ident_types</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">generics</span>: <span class="kw-2">&amp;</span><span class="ident">Generics</span>,
+    <span class="ident">inputs</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">FnArg</span><span class="op">&gt;</span>,
+    <span class="ident">output</span>: <span class="kw-2">&amp;</span><span class="ident">ReturnType</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">used</span>: <span class="ident">SearchSimpleTypeName</span> <span class="op">=</span> <span class="ident">Default::default</span>();
+    <span class="ident">used</span>.<span class="ident">visit_output</span>(<span class="ident">output</span>);
+    <span class="ident">used</span>.<span class="ident">visit_inputs</span>(<span class="ident">inputs</span>);
+    <span class="kw">let</span> <span class="ident">references</span> <span class="op">=</span> <span class="ident">extract_references_map</span>(<span class="ident">generics</span>);
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">used</span> <span class="op">=</span> <span class="ident">used</span>.<span class="ident">take</span>();
+    <span class="kw">let</span> <span class="ident">input_output</span> <span class="op">=</span> <span class="ident">used</span>.<span class="ident">clone</span>();
+    <span class="comment">// Extend the input output collected ref with the transitive ones:</span>
+    <span class="ident">used</span>.<span class="ident">extend</span>(
+        <span class="ident">generics</span>
+            .<span class="ident">params</span>
+            .<span class="ident">iter</span>()
+            .<span class="ident">filter_map</span>(<span class="ident">MaybeIdent::maybe_ident</span>)
+            .<span class="ident">filter</span>(<span class="op">|</span><span class="kw-2">&amp;</span><span class="ident">id</span><span class="op">|</span> <span class="ident">is_used</span>(<span class="ident">id</span>, <span class="kw-2">&amp;</span><span class="ident">references</span>, <span class="kw-2">&amp;</span><span class="ident">input_output</span>))
+            .<span class="ident">cloned</span>(),
+    );
+    <span class="ident">used</span>
+}
+
+<span class="kw">fn</span> <span class="ident">filtered_predicates</span>(<span class="kw-2">mut</span> <span class="ident">wc</span>: <span class="ident">syn::WhereClause</span>, <span class="ident">valids</span>: <span class="kw-2">&amp;</span><span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::WhereClause</span> {
+    <span class="ident">wc</span>.<span class="ident">predicates</span> <span class="op">=</span> <span class="ident">wc</span>
+        .<span class="ident">predicates</span>
+        .<span class="ident">clone</span>()
+        .<span class="ident">into_iter</span>()
+        .<span class="ident">filter</span>(<span class="op">|</span><span class="ident">wp</span><span class="op">|</span> {
+            <span class="ident">wp</span>.<span class="ident">maybe_ident</span>()
+                .<span class="ident">map</span>(<span class="op">|</span><span class="ident">t</span><span class="op">|</span> <span class="ident">valids</span>.<span class="ident">contains</span>(<span class="ident">t</span>))
+                .<span class="ident">unwrap_or_default</span>()
+        })
+        .<span class="ident">collect</span>();
+    <span class="ident">wc</span>
+}
+
+<span class="kw">fn</span> <span class="ident">filtered_generics</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">params</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="ident">syn::GenericParam</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span>,
+    <span class="ident">valids</span>: <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">HashSet</span><span class="op">&lt;</span><span class="ident">Ident</span><span class="op">&gt;</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="ident">syn::GenericParam</span><span class="op">&gt;</span> <span class="op">+</span> <span class="lifetime">&#39;a</span> {
+    <span class="ident">params</span>.<span class="ident">filter</span>(<span class="kw">move</span> <span class="op">|</span><span class="ident">p</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">p</span>.<span class="ident">maybe_ident</span>() {
+        <span class="prelude-val">Some</span>(<span class="ident">id</span>) <span class="op">=</span><span class="op">&gt;</span> <span class="ident">valids</span>.<span class="ident">contains</span>(<span class="ident">id</span>),
+        <span class="prelude-val">None</span> <span class="op">=</span><span class="op">&gt;</span> <span class="bool-val">false</span>,
+    })
+}
+
+<span class="comment">//noinspection RsTypeCheck</span>
+<span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">fn</span> <span class="ident">generics_clean_up</span><span class="op">&lt;</span><span class="lifetime">&#39;a</span><span class="op">&gt;</span>(
+    <span class="ident">original</span>: <span class="kw-2">&amp;</span><span class="ident">Generics</span>,
+    <span class="ident">inputs</span>: <span class="kw">impl</span> <span class="ident">Iterator</span><span class="op">&lt;</span><span class="ident">Item</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="lifetime">&#39;a</span> <span class="ident">FnArg</span><span class="op">&gt;</span>,
+    <span class="ident">output</span>: <span class="kw-2">&amp;</span><span class="ident">ReturnType</span>,
+) <span class="op">-</span><span class="op">&gt;</span> <span class="ident">syn::Generics</span> {
+    <span class="kw">let</span> <span class="ident">used</span> <span class="op">=</span> <span class="ident">references_ident_types</span>(<span class="ident">original</span>, <span class="ident">inputs</span>, <span class="ident">output</span>);
+    <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">result</span>: <span class="ident">Generics</span> <span class="op">=</span> <span class="ident">original</span>.<span class="ident">clone</span>();
+    <span class="ident">result</span>.<span class="ident">params</span> <span class="op">=</span> <span class="ident">filtered_generics</span>(<span class="ident">result</span>.<span class="ident">params</span>.<span class="ident">into_iter</span>(), <span class="kw-2">&amp;</span><span class="ident">used</span>).<span class="ident">collect</span>();
+    <span class="ident">result</span>.<span class="ident">where_clause</span> <span class="op">=</span> <span class="ident">result</span>.<span class="ident">where_clause</span>.<span class="ident">map</span>(<span class="op">|</span><span class="ident">wc</span><span class="op">|</span> <span class="ident">filtered_predicates</span>(<span class="ident">wc</span>, <span class="kw-2">&amp;</span><span class="ident">used</span>));
+    <span class="ident">result</span>
+}
+
+<span class="comment">// If type is not self and doesn&#39;t starts with :: return the first ident</span>
+<span class="comment">// of its path segment: only if is a simple path.</span>
+<span class="comment">// If type is a simple ident just return the this ident. That is useful to</span>
+<span class="comment">// find the base type for associate type indication</span>
+<span class="kw">fn</span> <span class="ident">first_type_path_segment_ident</span>(<span class="ident">t</span>: <span class="kw-2">&amp;</span><span class="ident">Type</span>) <span class="op">-</span><span class="op">&gt;</span> <span class="prelude-ty">Option</span><span class="op">&lt;</span><span class="kw-2">&amp;</span><span class="ident">Ident</span><span class="op">&gt;</span> {
+    <span class="kw">match</span> <span class="ident">t</span> {
+        <span class="ident">Type::Path</span>(<span class="ident">tp</span>) <span class="kw">if</span> <span class="ident">tp</span>.<span class="ident">qself</span>.<span class="ident">is_none</span>() <span class="op">&amp;&amp;</span> <span class="ident">tp</span>.<span class="ident">path</span>.<span class="ident">leading_colon</span>.<span class="ident">is_none</span>() <span class="op">=</span><span class="op">&gt;</span> <span class="ident">tp</span>
+            .<span class="ident">path</span>
+            .<span class="ident">segments</span>
+            .<span class="ident">iter</span>()
+            .<span class="ident">nth</span>(<span class="number">0</span>)
+            .<span class="ident">and_then</span>(<span class="op">|</span><span class="ident">ps</span><span class="op">|</span> <span class="kw">match</span> <span class="ident">ps</span>.<span class="ident">arguments</span> {
+                <span class="ident">syn::PathArguments::None</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">Some</span>(<span class="kw-2">&amp;</span><span class="ident">ps</span>.<span class="ident">ident</span>),
+                <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+            }),
+        <span class="kw">_</span> <span class="op">=</span><span class="op">&gt;</span> <span class="prelude-val">None</span>,
+    }
+}
+
+<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">test</span>)]</span>
+<span class="kw">mod</span> <span class="ident">test</span> {
+    <span class="kw">use</span> <span class="ident">syn::parse_quote</span>;
+
+    <span class="kw">use</span> <span class="kw">super</span>::<span class="kw-2">*</span>;
+    <span class="kw">use</span> <span class="kw">crate</span><span class="ident">::test</span>::{<span class="ident">assert_eq</span>, <span class="kw-2">*</span>};
+    <span class="kw">use</span> <span class="ident">mytest::rstest</span>;
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">fn_args_idents_should</span>() {
+        <span class="kw">let</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="macro">parse_quote!</span> {
+            <span class="kw">fn</span> <span class="ident">the_functon</span>(<span class="ident">first</span>: <span class="ident">u32</span>, <span class="ident">second</span>: <span class="ident">u32</span>) {}
+        };
+
+        <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">args</span> <span class="op">=</span> <span class="ident">fn_args_idents</span>(<span class="kw-2">&amp;</span><span class="ident">item_fn</span>);
+
+        <span class="macro">assert_eq!</span>(<span class="string">&quot;first&quot;</span>, <span class="ident">args</span>.<span class="ident">next</span>().<span class="ident">unwrap</span>().<span class="ident">to_string</span>());
+        <span class="macro">assert_eq!</span>(<span class="string">&quot;second&quot;</span>, <span class="ident">args</span>.<span class="ident">next</span>().<span class="ident">unwrap</span>().<span class="ident">to_string</span>());
+    }
+
+    <span class="attribute">#[<span class="ident">test</span>]</span>
+    <span class="kw">fn</span> <span class="ident">fn_args_has_ident_should</span>() {
+        <span class="kw">let</span> <span class="ident">item_fn</span> <span class="op">=</span> <span class="macro">parse_quote!</span> {
+            <span class="kw">fn</span> <span class="ident">the_functon</span>(<span class="ident">first</span>: <span class="ident">u32</span>, <span class="ident">second</span>: <span class="ident">u32</span>) {}
+        };
+
+        <span class="macro">assert!</span>(<span class="ident">fn_args_has_ident</span>(<span class="kw-2">&amp;</span><span class="ident">item_fn</span>, <span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;first&quot;</span>)));
+        <span class="macro">assert!</span>(<span class="op">!</span><span class="ident">fn_args_has_ident</span>(<span class="kw-2">&amp;</span><span class="ident">item_fn</span>, <span class="kw-2">&amp;</span><span class="ident">ident</span>(<span class="string">&quot;third&quot;</span>)));
+    }
+
+    <span class="attribute">#[<span class="ident">rstest</span>]</span>
+    <span class="attribute">#[<span class="ident">case::base</span>(<span class="string">&quot;fn foo&lt;A, B, C&gt;(a: A) -&gt; B {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::use_const_in_array</span>(<span class="string">&quot;fn foo&lt;A, const B: usize, C&gt;(a: A) -&gt; [u32; B] {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;u32&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::in_type_args</span>(<span class="string">&quot;fn foo&lt;A, const B: usize, C&gt;(a: A) -&gt; SomeType&lt;B&gt; {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::in_type_args</span>(<span class="string">&quot;fn foo&lt;A, const B: usize, C&gt;(a: SomeType&lt;A&gt;, b: SomeType&lt;B&gt;) {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::pointers</span>(<span class="string">&quot;fn foo&lt;A, B, C&gt;(a: *const A, b: &amp;B) {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::lifetime</span>(<span class="string">&quot;fn foo&lt;&#39;a, A, B, C&gt;(a: A, b: &amp;&#39;a B) {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_lifetime</span>(<span class="string">&quot;fn foo&lt;&#39;a, A, B, C&gt;(a: A, b: B) where B: Iterator&lt;Item=A&gt; + &#39;a {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::associated</span>(<span class="string">&quot;fn foo&lt;&#39;a, A:Copy, C&gt;(b: impl Iterator&lt;Item=A&gt; + &#39;a) {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;A&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_in_defs</span>(<span class="string">&quot;fn foo&lt;A:Copy, B: Iterator&lt;Item=A&gt;&gt;(b: B) {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_in_where</span>(<span class="string">&quot;fn foo&lt;A:Copy, B&gt;(b: B) where B: Iterator&lt;Item=A&gt; {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_const</span>(<span class="string">&quot;fn foo&lt;const A: usize, B, C&gt;(b: B) where B: Some&lt;A&gt; {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_lifetime</span>(<span class="string">&quot;fn foo&lt;&#39;a, A, B, C&gt;(a: A, b: B) where B: Iterator&lt;Item=A&gt; + &#39;a {}&quot;</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="attribute">#[<span class="ident">case::transitive_lifetime</span>(<span class="string">r#&quot;fn foo&lt;&#39;a, &#39;b, &#39;c, &#39;d, A, B, C&gt;
+        (a: A, b: B) 
+        where B: Iterator&lt;Item=A&gt; + &#39;c, 
+        &#39;c: &#39;a + &#39;b {}&quot;#</span>, <span class="kw-2">&amp;</span>[<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>, <span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>]</span>)]
+    <span class="kw">fn</span> <span class="ident">references_ident_types_should</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">f</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="kw-2">&amp;</span>[<span class="kw-2">&amp;</span><span class="ident">str</span>]) {
+        <span class="kw">let</span> <span class="ident">f</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="ident">f</span>.<span class="ident">ast</span>();
+        <span class="kw">let</span> <span class="ident">used</span> <span class="op">=</span> <span class="ident">references_ident_types</span>(<span class="kw-2">&amp;</span><span class="ident">f</span>.<span class="ident">sig</span>.<span class="ident">generics</span>, <span class="ident">f</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>(), <span class="kw-2">&amp;</span><span class="ident">f</span>.<span class="ident">sig</span>.<span class="ident">output</span>);
+
+        <span class="kw">let</span> <span class="ident">expected</span> <span class="op">=</span> <span class="macro">to_idents!</span>(<span class="ident">expected</span>)
+            .<span class="ident">into_iter</span>()
+            .<span class="ident">collect</span>::<span class="op">&lt;</span><span class="ident">std::collections::HashSet</span><span class="op">&lt;</span><span class="kw">_</span><span class="op">&gt;</span><span class="op">&gt;</span>();
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>, <span class="ident">used</span>);
+    }
+
+    <span class="attribute">#[<span class="ident">rstest</span>]</span>
+    <span class="attribute">#[<span class="ident">case::remove_not_in_output</span>(
+        <span class="string">r#&quot;fn test&lt;R: AsRef&lt;str&gt;, B, F, H: Iterator&lt;Item=u32&gt;&gt;() -&gt; (H, B, String, &amp;str)
+                        where F: ToString,
+                        B: Borrow&lt;u32&gt;
+                        {}&quot;#</span>,
+        <span class="string">r#&quot;fn test&lt;B, H: Iterator&lt;Item=u32&gt;&gt;() -&gt; (H, B, String, &amp;str)
+                        where B: Borrow&lt;u32&gt;
+                {}&quot;#</span>
+    )]</span>
+    <span class="attribute">#[<span class="ident">case::not_remove_used_in_arguments</span>(
+        <span class="string">r#&quot;fn test&lt;R: AsRef&lt;str&gt;, B, F, H: Iterator&lt;Item=u32&gt;&gt;
+                    (h: H, it: impl Iterator&lt;Item=R&gt;, j: &amp;[B])
+                    where F: ToString,
+                    B: Borrow&lt;u32&gt;
+                {}&quot;#</span>,
+        <span class="string">r#&quot;fn test&lt;R: AsRef&lt;str&gt;, B, H: Iterator&lt;Item=u32&gt;&gt;
+                    (h: H, it: impl Iterator&lt;Item=R&gt;, j: &amp;[B])
+                    where
+                    B: Borrow&lt;u32&gt;
+                {}&quot;#</span>
+    )]</span>
+    <span class="attribute">#[<span class="ident">case::dont_remove_transitive</span>(
+        <span class="string">r#&quot;fn test&lt;A, B, C, D, const F: usize, O&gt;(a: A) where 
+            B: AsRef&lt;C&gt;,
+            A: Iterator&lt;Item=[B; F]&gt;,
+            D: ArsRef&lt;O&gt; {}&quot;#</span>,
+        <span class="string">r#&quot;fn test&lt;A, B, C, const F: usize&gt;(a: A) where 
+            B: AsRef&lt;C&gt;,
+            A: Iterator&lt;Item=[B; F]&gt; {}&quot;#</span>
+    )]</span>
+    <span class="attribute">#[<span class="ident">case::remove_unused_lifetime</span>(
+        <span class="string">&quot;fn test&lt;&#39;a, &#39;b, &#39;c, &#39;d, &#39;e, &#39;f, &#39;g, A&gt;(a: &amp;&#39;a uint32, b: impl AsRef&lt;A&gt; + &#39;b) where &#39;b: &#39;c + &#39;d, A: Copy + &#39;e, &#39;f: &#39;g {}&quot;</span>,
+        <span class="string">&quot;fn test&lt;&#39;a, &#39;b, &#39;c, &#39;d, &#39;e, A&gt;(a: &amp;&#39;a uint32, b: impl AsRef&lt;A&gt; + &#39;b) where &#39;b: &#39;c + &#39;d, A: Copy + &#39;e {}&quot;</span>
+    )]</span>
+    <span class="attribute">#[<span class="ident">case::remove_unused_const</span>(
+        <span class="string">r#&quot;fn test&lt;const A: usize, const B: usize, const C: usize, const D: usize, T, O&gt;
+            (a: [u32; A], b: SomeType&lt;B&gt;, c: T) where 
+            T: Iterator&lt;Item=[i32; C]&gt;,
+            O: AsRef&lt;D&gt; 
+            {}&quot;#</span>,
+        <span class="string">r#&quot;fn test&lt;const A: usize, const B: usize, const C: usize, T&gt;
+            (a: [u32; A], b: SomeType&lt;B&gt;, c: T) where 
+            T: Iterator&lt;Item=[i32; C]&gt;
+            {}&quot;#</span>
+    )]</span>
+    <span class="kw">fn</span> <span class="ident">generics_cleaner</span>(<span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">code</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>, <span class="attribute">#[<span class="ident">case</span>]</span> <span class="ident">expected</span>: <span class="kw-2">&amp;</span><span class="ident">str</span>) {
+        <span class="comment">// Should remove all generics parameters that are not present in output</span>
+        <span class="kw">let</span> <span class="ident">item_fn</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="ident">code</span>.<span class="ident">ast</span>();
+
+        <span class="kw">let</span> <span class="ident">expected</span>: <span class="ident">ItemFn</span> <span class="op">=</span> <span class="ident">expected</span>.<span class="ident">ast</span>();
+
+        <span class="kw">let</span> <span class="ident">cleaned</span> <span class="op">=</span> <span class="ident">generics_clean_up</span>(
+            <span class="kw-2">&amp;</span><span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">generics</span>,
+            <span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">inputs</span>.<span class="ident">iter</span>(),
+            <span class="kw-2">&amp;</span><span class="ident">item_fn</span>.<span class="ident">sig</span>.<span class="ident">output</span>,
+        );
+
+        <span class="macro">assert_eq!</span>(<span class="ident">expected</span>.<span class="ident">sig</span>.<span class="ident">generics</span>, <span class="ident">cleaned</span>);
+    }
+}
+</pre></div>
+</section><section id="search" class="content hidden"></section><div id="rustdoc-vars" data-root-path="../../" data-current-crate="rstest" data-search-index-js="../../search-index.js" data-search-js="../../search.js"></div>
+    <script src="../../main.js"></script><script src="../../source-script.js"></script><script src="../../source-files.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/storage.js b/third_party/rust/rstest/v0_12/crate/docs/head/storage.js
new file mode 100644
index 0000000..6b16cbd
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/storage.js
@@ -0,0 +1 @@
+var resourcesSuffix="";var darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");window.mainTheme=document.getElementById("mainThemeStyle");var settingsDataset=(function(){var settingsElement=document.getElementById("default-settings");if(settingsElement===null){return null}var dataset=settingsElement.dataset;if(dataset===undefined){return null}return dataset})();function getSettingValue(settingName){var current=getCurrentValue('rustdoc-'+settingName);if(current!==null){return current}if(settingsDataset!==null){var def=settingsDataset[settingName.replace(/-/g,'_')];if(def!==undefined){return def}}return null}var localStoredTheme=getSettingValue("theme");var savedHref=[];function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(!elem||!elem.classList){return}elem.classList.add(className)}function removeClass(elem,className){if(!elem||!elem.classList){return}elem.classList.remove(className)}function onEach(arr,func,reversed){if(arr&&arr.length>0&&func){var length=arr.length;var i;if(reversed){for(i=length-1;i>=0;--i){if(func(arr[i])){return true}}}else{for(i=0;i<length;++i){if(func(arr[i])){return true}}}}return false}function onEachLazy(lazyArray,func,reversed){return onEach(Array.prototype.slice.call(lazyArray),func,reversed)}function hasOwnPropertyRustdoc(obj,property){return Object.prototype.hasOwnProperty.call(obj,property)}function updateLocalStorage(name,value){try{window.localStorage.setItem(name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem(name)}catch(e){return null}}function switchTheme(styleElem,mainStyleElem,newTheme,saveTheme){var fullBasicCss="rustdoc"+resourcesSuffix+".css";var fullNewTheme=newTheme+resourcesSuffix+".css";var newHref=mainStyleElem.href.replace(fullBasicCss,fullNewTheme);if(saveTheme){updateLocalStorage("rustdoc-theme",newTheme)}if(styleElem.href===newHref){return}var found=false;if(savedHref.length===0){onEachLazy(document.getElementsByTagName("link"),function(el){savedHref.push(el.href)})}onEach(savedHref,function(el){if(el===newHref){found=true;return true}});if(found){styleElem.href=newHref}}function useSystemTheme(value){if(value===undefined){value=true}updateLocalStorage("rustdoc-use-system-theme",value);var toggle=document.getElementById("use-system-theme");if(toggle&&toggle instanceof HTMLInputElement){toggle.checked=value}}var updateSystemTheme=(function(){if(!window.matchMedia){return function(){var cssTheme=getComputedStyle(document.documentElement).getPropertyValue('content');switchTheme(window.currentTheme,window.mainTheme,JSON.parse(cssTheme)||"light",true)}}var mql=window.matchMedia("(prefers-color-scheme: dark)");function handlePreferenceChange(mql){if(getSettingValue("use-system-theme")!=="false"){var lightTheme=getSettingValue("preferred-light-theme")||"light";var darkTheme=getSettingValue("preferred-dark-theme")||"dark";if(mql.matches){switchTheme(window.currentTheme,window.mainTheme,darkTheme,true)}else{switchTheme(window.currentTheme,window.mainTheme,lightTheme,true)}}}mql.addListener(handlePreferenceChange);return function(){handlePreferenceChange(mql)}})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("rustdoc-preferred-dark-theme",localStoredTheme)}updateSystemTheme()}else{switchTheme(window.currentTheme,window.mainTheme,getSettingValue("theme")||"light",false)}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/theme.js b/third_party/rust/rstest/v0_12/crate/docs/head/theme.js
new file mode 100644
index 0000000..ebd1a87
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/theme.js
@@ -0,0 +1 @@
+var themes=document.getElementById("theme-choices");var themePicker=document.getElementById("theme-picker");function showThemeButtonState(){themes.style.display="block";themePicker.style.borderBottomRightRadius="0";themePicker.style.borderBottomLeftRadius="0"}function hideThemeButtonState(){themes.style.display="none";themePicker.style.borderBottomRightRadius="3px";themePicker.style.borderBottomLeftRadius="3px"}function switchThemeButtonState(){if(themes.style.display==="block"){hideThemeButtonState()}else{showThemeButtonState()}};function handleThemeButtonsBlur(e){var active=document.activeElement;var related=e.relatedTarget;if(active.id!=="themePicker"&&(!active.parentNode||active.parentNode.id!=="theme-choices")&&(!related||(related.id!=="themePicker"&&(!related.parentNode||related.parentNode.id!=="theme-choices")))){hideThemeButtonState()}}themePicker.onclick=switchThemeButtonState;themePicker.onblur=handleThemeButtonsBlur;["dark","light"].forEach(function(item){var but=document.createElement('button');but.textContent=item;but.onclick=function(el){switchTheme(currentTheme,mainTheme,item,true)};but.onblur=handleThemeButtonsBlur;themes.appendChild(but)})
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/docs/head/wheel.svg b/third_party/rust/rstest/v0_12/crate/docs/head/wheel.svg
new file mode 100644
index 0000000..01da3b2
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/docs/head/wheel.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Capa_1" width="27.434" height="29.5" enable-background="new 0 0 27.434 29.5" version="1.1" viewBox="0 0 27.434 29.5" xml:space="preserve"><g><path d="M27.315,18.389c-0.165-0.604-0.509-1.113-0.981-1.459c-0.042-0.144-0.083-0.429-0.015-0.761l0.037-0.177v-0.182V14.8 c0-1.247-0.006-1.277-0.048-1.472c-0.076-0.354-0.035-0.653,0.007-0.803c0.477-0.346,0.828-0.861,0.996-1.476 c0.261-0.956,0.076-2.091-0.508-3.114l-0.591-1.032c-0.746-1.307-1.965-2.119-3.182-2.119c-0.378,0-0.75,0.081-1.085,0.235 c-0.198-0.025-0.554-0.15-0.855-0.389l-0.103-0.082l-0.114-0.065l-1.857-1.067L18.92,3.36l-0.105-0.044 c-0.376-0.154-0.658-0.41-0.768-0.556C17.918,1.172,16.349,0,14.296,0H13.14c-2.043,0-3.608,1.154-3.749,2.721 C9.277,2.862,8.999,3.104,8.633,3.25l-0.1,0.039L8.439,3.341L6.495,4.406L6.363,4.479L6.245,4.573 C5.936,4.82,5.596,4.944,5.416,4.977c-0.314-0.139-0.66-0.21-1.011-0.21c-1.198,0-2.411,0.819-3.165,2.139L0.65,7.938 c-0.412,0.72-0.642,1.521-0.644,2.258c-0.003,0.952,0.362,1.756,1.013,2.256c0.034,0.155,0.061,0.448-0.016,0.786 c-0.038,0.168-0.062,0.28-0.062,1.563c0,1.148,0,1.148,0.015,1.262l0.009,0.073l0.017,0.073c0.073,0.346,0.045,0.643,0.011,0.802 C0.348,17.512-0.01,18.314,0,19.268c0.008,0.729,0.238,1.523,0.648,2.242l0.589,1.031c0.761,1.331,1.967,2.159,3.15,2.159 c0.324,0,0.645-0.064,0.938-0.187c0.167,0.038,0.492,0.156,0.813,0.416l0.11,0.088l0.124,0.07l2.045,1.156l0.102,0.057l0.107,0.043 c0.364,0.147,0.646,0.381,0.766,0.521c0.164,1.52,1.719,2.634,3.745,2.634h1.155c2.037,0,3.598-1.134,3.747-2.675 c0.117-0.145,0.401-0.393,0.774-0.549l0.111-0.047l0.105-0.062l1.96-1.159l0.105-0.062l0.097-0.075 c0.309-0.246,0.651-0.371,0.832-0.402c0.313,0.138,0.662,0.212,1.016,0.212c1.199,0,2.412-0.82,3.166-2.139l0.59-1.032 C27.387,20.48,27.575,19.342,27.315,18.389z M25.274,20.635l-0.59,1.032c-0.438,0.765-1.104,1.251-1.639,1.251 c-0.133,0-0.258-0.029-0.369-0.094c-0.15-0.086-0.346-0.127-0.566-0.127c-0.596,0-1.383,0.295-2.01,0.796l-1.96,1.157 c-1.016,0.425-1.846,1.291-1.846,1.929s-0.898,1.159-1.998,1.159H13.14c-1.1,0-1.998-0.514-1.998-1.141s-0.834-1.477-1.854-1.888 l-2.046-1.157c-0.636-0.511-1.425-0.814-2.006-0.814c-0.202,0-0.379,0.037-0.516,0.115c-0.101,0.057-0.214,0.084-0.333,0.084 c-0.518,0-1.179-0.498-1.62-1.271l-0.591-1.032c-0.545-0.954-0.556-1.983-0.024-2.286c0.532-0.305,0.78-1.432,0.551-2.506 c0,0,0-0.003,0-1.042c0-1.088,0.021-1.18,0.021-1.18c0.238-1.072-0.01-2.203-0.552-2.513C1.631,10.8,1.634,9.765,2.18,8.812 L2.769,7.78c0.438-0.766,1.103-1.251,1.636-1.251c0.131,0,0.255,0.029,0.365,0.092C4.92,6.707,5.114,6.747,5.334,6.747 c0.596,0,1.38-0.296,2.007-0.795l1.944-1.065c1.021-0.407,1.856-1.277,1.856-1.933c0-0.656,0.898-1.192,1.998-1.192h1.156V1.761 c1.1,0,1.998,0.545,1.998,1.211c0,0.667,0.832,1.554,1.849,1.973L20,6.013c0.618,0.489,1.401,0.775,2.012,0.775 c0.24,0,0.454-0.045,0.62-0.139c0.122-0.069,0.259-0.102,0.403-0.102c0.551,0,1.221,0.476,1.653,1.231l0.59,1.032 c0.544,0.953,0.518,2.004-0.062,2.334c-0.577,0.331-0.859,1.48-0.627,2.554c0,0,0.01,0.042,0.01,1.103c0,1.012,0,1.012,0,1.012 c-0.218,1.049,0.068,2.174,0.636,2.498C25.802,18.635,25.819,19.68,25.274,20.635z"/><path d="M13.61,7.611c-3.913,0-7.084,3.173-7.084,7.085c0,3.914,3.171,7.085,7.084,7.085s7.085-3.172,7.085-7.085 C20.695,10.784,17.523,7.611,13.61,7.611z M13.61,20.02c-2.936,0-5.323-2.388-5.323-5.323c0-2.935,2.388-5.323,5.323-5.323 s5.324,2.388,5.324,5.323C18.934,17.632,16.546,20.02,13.61,20.02z"/><path d="M13.682,9.908c-2.602,0-4.718,2.116-4.718,4.718c0,2.601,2.116,4.716,4.718,4.716c2.601,0,4.717-2.115,4.717-4.716 C18.399,12.024,16.283,9.908,13.682,9.908z M13.682,17.581c-1.633,0-2.956-1.323-2.956-2.955s1.323-2.956,2.956-2.956 c1.632,0,2.956,1.324,2.956,2.956S15.314,17.581,13.682,17.581z"/></g></svg>
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/notes.md b/third_party/rust/rstest/v0_12/crate/notes.md
new file mode 100644
index 0000000..eccc171
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/notes.md
@@ -0,0 +1,107 @@
+You have to write a file parser. The first test is:
+
+```rust
+use std::path::{PathBuf, Path};
+
+#[derive(Default, Debug, PartialEq)]
+struct Matrix { rows: usize, cols: usize, data: Vec<i32> }
+
+impl<P: AsRef<Path>> From<P> for Matrix { fn from(_: P) -> Self { Default::default() } }
+
+fn create_empty_file() -> PathBuf { "".into() }
+
+#[test]
+fn should_parse_empty_file() {
+     let empty = create_empty_file();
+
+     let matrix: Matrix = Matrix::from(empty);
+
+     assert_eq!(matrix, Default::default())
+}
+```
+
+Ok, `create_empty_file()` should do a lot of job and can be reused in 
+other tests and fixtures: it's a good candidate to be a fixture. 
+Let's rewrite previous example by use `rstest`: we'll use `empty` for 
+fixture instead of `create_empty_file()` because when we use as fixture 
+will be an object instead an action.
+
+```rust
+use std::path::{PathBuf, Path};
+
+use rstest::*;
+
+#[derive(Default, Debug, PartialEq)]
+struct Matrix { rows: usize, cols: usize, data: Vec<i32> }
+impl<P: AsRef<Path>> From<P> for Matrix { fn from(_: P) -> Self { Default::default() } }
+
+#[fixture]
+fn empty() -> PathBuf {
+     let path = "empty_file".into();
+     std::fs::File::create(&path).expect("Cannot open");
+     path
+}
+
+#[rstest]
+fn should_parse_empty_file(empty: PathBuf) {
+     let matrix: Matrix = Matrix::from(empty);
+
+     assert_eq!(matrix, Default::default())
+}
+```
+
+Now our `Matrix`'s `From` trait is a little bit more generic and can 
+take every struct that implement `AsRef<Path>` like `PathBuf` do. So we 
+can generalize our test too:
+
+```rust
+use std::path::{PathBuf, Path};
+
+use rstest::*;
+
+#[derive(Default, Debug, PartialEq)]
+struct Matrix { rows: usize, cols: usize, data: Vec<i32> }
+
+impl<P: AsRef<Path>> From<P> for Matrix { fn from(_: P) -> Self { Default::default() } }
+
+#[rstest]
+fn should_parse_empty_file<P>(empty: P)
+     where P: AsRef<Path>
+{
+     let matrix: Matrix = Matrix::from(empty);
+
+     assert_eq!(matrix, Default::default())
+}
+```
+
+Our test is neat and clear but we have a big issue to fix: we cannot 
+leave `"empty_file"` on our disk, we must remove it we are done! 
+That is a job for Rust ownership!!
+
+We should just wrap `PathBuf` in out `TempFile` struct and implement 
+both `Drop` and `AsRef<Path>` traits. We'll use `Drop` to delete it.
+
+```rust
+use std::path::{PathBuf, Path};
+
+use rstest::*;
+
+struct TempFile(PathBuf);
+
+impl Drop for TempFile {fn drop(&mut self) {
+         std::fs::remove_file(&self.0).unwrap();
+     }
+}
+
+impl AsRef<Path> for TempFile {
+    fn as_ref(&self) -> &Path {
+        &self.0
+    }
+}
+
+#[fixture]
+fn empty() -> TempFile {
+     let path = "empty_file".into();
+     std::fs::File::create(&path).expect("Cannot open");
+     TempFile(path)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/error.rs b/third_party/rust/rstest/v0_12/crate/src/error.rs
new file mode 100644
index 0000000..c9fc6f34
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/error.rs
@@ -0,0 +1,274 @@
+/// Module for error rendering stuff
+use std::collections::HashMap;
+
+use proc_macro2::TokenStream;
+use syn::{spanned::Spanned, visit::Visit};
+use syn::{visit, ItemFn};
+
+use crate::parse::{
+    fixture::FixtureInfo,
+    rstest::{RsTestData, RsTestInfo},
+};
+use crate::refident::MaybeIdent;
+
+use super::utils::fn_args_has_ident;
+
+pub(crate) fn rstest(test: &ItemFn, info: &RsTestInfo) -> TokenStream {
+    missed_arguments(test, info.data.items.iter())
+        .chain(duplicate_arguments(info.data.items.iter()))
+        .chain(invalid_cases(&info.data))
+        .chain(case_args_without_cases(&info.data))
+        .map(|e| e.to_compile_error())
+        .collect()
+}
+
+pub(crate) fn fixture(test: &ItemFn, info: &FixtureInfo) -> TokenStream {
+    missed_arguments(test, info.data.items.iter())
+        .chain(duplicate_arguments(info.data.items.iter()))
+        .chain(async_once(test, info))
+        .chain(generics_once(test, info))
+        .map(|e| e.to_compile_error())
+        .collect()
+}
+
+fn async_once<'a>(test: &'a ItemFn, info: &FixtureInfo) -> Errors<'a> {
+    match (test.sig.asyncness, info.attributes.get_once()) {
+        (Some(_asyncness), Some(once)) => Box::new(std::iter::once(syn::Error::new(
+            once.span(),
+            "Cannot apply #[once] to async fixture.",
+        ))),
+        _ => Box::new(std::iter::empty()),
+    }
+}
+
+#[derive(Default)]
+struct SearchImpl(bool);
+
+impl<'ast> Visit<'ast> for SearchImpl {
+    fn visit_type(&mut self, i: &'ast syn::Type) {
+        if self.0 {
+            return;
+        }
+        if let syn::Type::ImplTrait(_) = i {
+            self.0 = true
+        }
+        visit::visit_type(self, i);
+    }
+}
+
+impl SearchImpl {
+    fn function_has_some_impl(f: &ItemFn) -> bool {
+        let mut s = SearchImpl::default();
+        visit::visit_item_fn(&mut s, f);
+        s.0
+    }
+}
+
+fn has_some_generics(test: &ItemFn) -> bool {
+    !test.sig.generics.params.is_empty() || SearchImpl::function_has_some_impl(test)
+}
+
+fn generics_once<'a>(test: &'a ItemFn, info: &FixtureInfo) -> Errors<'a> {
+    match (has_some_generics(test), info.attributes.get_once()) {
+        (true, Some(once)) => Box::new(std::iter::once(syn::Error::new(
+            once.span(),
+            "Cannot apply #[once] on generic fixture.",
+        ))),
+        _ => Box::new(std::iter::empty()),
+    }
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct ErrorsVec(Vec<syn::Error>);
+
+pub(crate) fn _merge_errors<R1, R2>(
+    r1: Result<R1, ErrorsVec>,
+    r2: Result<R2, ErrorsVec>,
+) -> Result<(R1, R2), ErrorsVec> {
+    match (r1, r2) {
+        (Ok(r1), Ok(r2)) => Ok((r1, r2)),
+        (Ok(_), Err(e)) | (Err(e), Ok(_)) => Err(e),
+        (Err(mut e1), Err(mut e2)) => {
+            e1.append(&mut e2);
+            Err(e1)
+        }
+    }
+}
+
+macro_rules! merge_errors {
+    ($e:expr) => {
+        $e
+    };
+    ($e:expr, $($es:expr), +) => {
+        crate::error::_merge_errors($e, merge_errors!($($es),*))
+    };
+}
+
+macro_rules! composed_tuple {
+    ($i:ident) => {
+        $i
+    };
+    ($i:ident, $($is:ident), +) => {
+        ($i, composed_tuple!($($is),*))
+    };
+}
+
+impl std::ops::Deref for ErrorsVec {
+    type Target = Vec<syn::Error>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl std::ops::DerefMut for ErrorsVec {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl From<syn::Error> for ErrorsVec {
+    fn from(errors: syn::Error) -> Self {
+        vec![errors].into()
+    }
+}
+
+impl From<Vec<syn::Error>> for ErrorsVec {
+    fn from(errors: Vec<syn::Error>) -> Self {
+        Self(errors)
+    }
+}
+
+impl From<ErrorsVec> for Vec<syn::Error> {
+    fn from(v: ErrorsVec) -> Self {
+        v.0
+    }
+}
+
+impl quote::ToTokens for ErrorsVec {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        tokens.extend(self.0.iter().map(|e| e.to_compile_error()))
+    }
+}
+
+impl From<ErrorsVec> for proc_macro::TokenStream {
+    fn from(v: ErrorsVec) -> Self {
+        use quote::ToTokens;
+        v.into_token_stream().into()
+    }
+}
+
+type Errors<'a> = Box<dyn Iterator<Item = syn::Error> + 'a>;
+
+fn missed_arguments<'a, I: MaybeIdent + Spanned + 'a>(
+    test: &'a ItemFn,
+    args: impl Iterator<Item = &'a I> + 'a,
+) -> Errors<'a> {
+    Box::new(
+        args.filter_map(|it| it.maybe_ident().map(|ident| (it, ident)))
+            .filter(move |(_, ident)| !fn_args_has_ident(test, ident))
+            .map(|(missed, ident)| {
+                syn::Error::new(
+                    missed.span(),
+                    &format!(
+                        "Missed argument: '{}' should be a test function argument.",
+                        ident
+                    ),
+                )
+            }),
+    )
+}
+
+fn duplicate_arguments<'a, I: MaybeIdent + Spanned + 'a>(
+    args: impl Iterator<Item = &'a I> + 'a,
+) -> Errors<'a> {
+    let mut used = HashMap::new();
+    Box::new(
+        args.filter_map(|it| it.maybe_ident().map(|ident| (it, ident)))
+            .filter_map(move |(it, ident)| {
+                let name = ident.to_string();
+                let is_duplicate = used.contains_key(&name);
+                used.insert(name, it);
+                match is_duplicate {
+                    true => Some((it, ident)),
+                    false => None,
+                }
+            })
+            .map(|(duplicate, ident)| {
+                syn::Error::new(
+                    duplicate.span(),
+                    &format!("Duplicate argument: '{}' is already defined.", ident),
+                )
+            }),
+    )
+}
+
+fn invalid_cases(params: &RsTestData) -> Errors {
+    let n_args = params.case_args().count();
+    Box::new(
+        params
+            .cases()
+            .filter(move |case| case.args.len() != n_args)
+            .map(|case| {
+                syn::Error::new_spanned(
+                    &case,
+                    "Wrong case signature: should match the given parameters list.",
+                )
+            }),
+    )
+}
+
+fn case_args_without_cases(params: &RsTestData) -> Errors {
+    if !params.has_cases() {
+        return Box::new(
+            params
+                .case_args()
+                .map(|a| syn::Error::new(a.span(), "No cases for this argument.")),
+        );
+    }
+    Box::new(std::iter::empty())
+}
+
+#[cfg(test)]
+mod test {
+    use crate::test::{assert_eq, *};
+    use rstest_test::assert_in;
+
+    use super::*;
+
+    #[rstest]
+    #[case::generics("fn f<G: SomeTrait>(){}")]
+    #[case::const_generics("fn f<const N: usize>(){}")]
+    #[case::lifetimes("fn f<'a>(){}")]
+    #[case::use_impl_in_answer("fn f() -> impl Iterator<Item=u32>{}")]
+    #[case::use_impl_in_argumets("fn f(it: impl Iterator<Item=u32>){}")]
+    #[should_panic]
+    #[case::sanity_check_with_no_generics("fn f() {}")]
+    fn generics_once_should_return_error(#[case] f: &str) {
+        let f: ItemFn = f.ast();
+        let info = FixtureInfo::default().with_once();
+
+        let errors = generics_once(&f, &info);
+
+        let out = errors
+            .map(|e| format!("{:?}", e))
+            .collect::<Vec<_>>()
+            .join("-----------------------\n");
+
+        assert_in!(out, "Cannot apply #[once] on generic fixture.");
+    }
+
+    #[rstest]
+    #[case::generics("fn f<G: SomeTrait>(){}")]
+    #[case::const_generics("fn f<const N: usize>(){}")]
+    #[case::lifetimes("fn f<'a>(){}")]
+    #[case::use_impl_in_answer("fn f() -> impl Iterator<Item=u32>{}")]
+    #[case::use_impl_in_argumets("fn f(it: impl Iterator<Item=u32>){}")]
+    fn generics_once_should_not_return_if_no_once(#[case] f: &str) {
+        let f: ItemFn = f.ast();
+        let info = FixtureInfo::default();
+
+        let errors = generics_once(&f, &info);
+
+        assert_eq!(0, errors.count());
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/lib.rs b/third_party/rust/rstest/v0_12/crate/src/lib.rs
new file mode 100644
index 0000000..64e70a0
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/lib.rs
@@ -0,0 +1,1231 @@
+//! This crate will help you to write simpler tests by leveraging a software testing concept called
+//! [test fixtures](https://en.wikipedia.org/wiki/Test_fixture#Software). A fixture is something
+//! that you can use in your tests to encapsulate a test's dependencies.
+//!
+//! The general idea is to have smaller tests that only describe the thing you're testing while you
+//! hide the auxiliary utilities your tests make use of somewhere else.
+//! For instance, if you have an application that has many tests with users, shopping baskets, and
+//! products, you'd have to create a user, a shopping basket, and product every single time in
+//! every test which becomes unwieldy quickly. In order to cut down on that repetition, you can
+//! instead use fixtures to declare that you need those objects for your function and the fixtures
+//! will take care of creating those by themselves. Focus on the important stuff in your tests!
+//!
+//! In `rstest` a fixture is a function that can return any kind of valid Rust type. This
+//! effectively means that your fixtures are not limited by the kind of data they can return.
+//! A test can consume an arbitrary number of fixtures at the same time.
+//!
+//! ## What
+//!
+//! The `rstest` crate defines the following procedural macros:
+//!
+//! - [`[rstest]`](macro@rstest): Declare that a test or a group of tests that may take
+//! [fixtures](attr.rstest.html#injecting-fixtures),
+//! [input table](attr.rstest.html#test-parametrized-cases) or
+//! [list of values](attr.rstest.html#values-lists).
+//! - [`[fixture]`](macro@fixture): To mark a function as a fixture.
+//!
+//! ## Why
+//!
+//! Very often in Rust we write tests like this
+//!
+//! ```
+//! #[test]
+//! fn should_process_two_users() {
+//!     let mut repository = create_repository();
+//!     repository.add("Bob", 21);
+//!     repository.add("Alice", 22);
+//!
+//!     let processor = string_processor();
+//!     processor.send_all(&repository, "Good Morning");
+//!
+//!     assert_eq!(2, processor.output.find("Good Morning").count());
+//!     assert!(processor.output.contains("Bob"));
+//!     assert!(processor.output.contains("Alice"));
+//! }
+//! ```
+//!
+//! By making use of [`[rstest]`](macro@rstest) we can isolate the dependencies `empty_repository` and
+//! `string_processor` by passing them as fixtures:
+//!
+//! ```
+//! # use rstest::*;
+//! #[rstest]
+//! fn should_process_two_users(mut empty_repository: impl Repository,
+//!                             string_processor: FakeProcessor) {
+//!     empty_repository.add("Bob", 21);
+//!     empty_repository.add("Alice", 22);
+//!
+//!     string_processor.send_all("Good Morning");
+//!
+//!     assert_eq!(2, string_processor.output.find("Good Morning").count());
+//!     assert!(string_processor.output.contains("Bob"));
+//!     assert!(string_processor.output.contains("Alice"));
+//! }
+//! ```
+//!
+//! ... or if you use `"Alice"` and `"Bob"` in other tests, you can isolate `alice_and_bob` fixture
+//! and use it directly:
+//!
+//! ```
+//! # use rstest::*;
+//! # trait Repository { fn add(&mut self, name: &str, age: u8); }
+//! # struct Rep;
+//! # impl Repository for Rep { fn add(&mut self, name: &str, age: u8) {} }
+//! # #[fixture]
+//! # fn empty_repository() -> Rep {
+//! #     Rep
+//! # }
+//! #[fixture]
+//! fn alice_and_bob(mut empty_repository: impl Repository) -> impl Repository {
+//!     empty_repository.add("Bob", 21);
+//!     empty_repository.add("Alice", 22);
+//!     empty_repository
+//! }
+//!
+//! #[rstest]
+//! fn should_process_two_users(alice_and_bob: impl Repository,
+//!                             string_processor: FakeProcessor) {
+//!     string_processor.send_all("Good Morning");
+//!
+//!     assert_eq!(2, string_processor.output.find("Good Morning").count());
+//!     assert!(string_processor.output.contains("Bob"));
+//!     assert!(string_processor.output.contains("Alice"));
+//! }
+//! ```
+//!
+//! ## Injecting fixtures as function arguments
+//!
+//! `rstest` functions can receive fixtures by using them as input arguments.
+//! A function decorated with [`[rstest]`](attr.rstest.html#injecting-fixtures)
+//! will resolve each argument name by call the fixture function.
+//! Fixtures should be annotated with the [`[fixture]`](macro@fixture) attribute.
+//!
+//! Fixtures will be resolved like function calls by following the standard resolution rules.
+//! Therefore, an identically named fixture can be use in different context.
+//!
+//! ```
+//! # use rstest::*;
+//! # trait Repository { }
+//! # #[derive(Default)]
+//! # struct DataSet {}
+//! # impl Repository for DataSet { }
+//! mod empty_cases {
+//! # use rstest::*;
+//! # trait Repository { }
+//! # #[derive(Default)]
+//! # struct DataSet {}
+//! # impl Repository for DataSet { }
+//!     use super::*;
+//!
+//!     #[fixture]
+//!     fn repository() -> impl Repository {
+//!         DataSet::default()
+//!     }
+//!
+//!     #[rstest]
+//!     fn should_do_nothing(repository: impl Repository) {
+//!         //.. test impl ..
+//!     }
+//! }
+//!
+//! mod non_trivial_case {
+//! # use rstest::*;
+//! # trait Repository { }
+//! # #[derive(Default)]
+//! # struct DataSet {}
+//! # impl Repository for DataSet { }
+//!     use super::*;
+//!
+//!     #[fixture]
+//!     fn repository() -> impl Repository {
+//!         let mut ds = DataSet::default();
+//!         // Fill your dataset with interesting case
+//!         ds
+//!     }
+//!
+//!     #[rstest]
+//!     fn should_notify_all_entries(repository: impl Repository) {
+//!         //.. test impl ..
+//!     }
+//! }
+//!
+//! ```
+//!
+//! Last but not least, fixtures can be injected like we saw in `alice_and_bob` example.
+//!
+//! ## Creating parametrized tests
+//!
+//! You can use also [`[rstest]`](attr.rstest.html#test-parametrized-cases) to create
+//! simple table-based tests. Let's see the classic Fibonacci example:
+//!
+//! ```
+//! use rstest::rstest;
+//!
+//! #[rstest]
+//! #[case(0, 0)]
+//! #[case(1, 1)]
+//! #[case(2, 1)]
+//! #[case(3, 2)]
+//! #[case(4, 3)]
+//! #[case(5, 5)]
+//! #[case(6, 8)]
+//! fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {
+//!     assert_eq!(expected, fibonacci(input))
+//! }
+//!
+//! fn fibonacci(input: u32) -> u32 {
+//!     match input {
+//!         0 => 0,
+//!         1 => 1,
+//!         n => fibonacci(n - 2) + fibonacci(n - 1)
+//!     }
+//! }
+//! ```
+//! This will generate a bunch of tests, one for every `#[case(a, b)]`.
+//!
+//! ## Creating a test for each combinations of given values
+//!
+//! In some cases you need to test your code for each combinations of some input values. In this
+//! cases [`[rstest]`](attr.rstest.html#values-lists) give you the ability to define a list
+//! of values (rust expressions) to use for an arguments.
+//!
+//! ```
+//! # use rstest::rstest;
+//! # #[derive(PartialEq, Debug)]
+//! # enum State { Init, Start, Processing, Terminated }
+//! # #[derive(PartialEq, Debug)]
+//! # enum Event { Error, Fatal }
+//! # impl State { fn process(self, event: Event) -> Self { self } }
+//!
+//! #[rstest]
+//! fn should_terminate(
+//!     #[values(State::Init, State::Start, State::Processing)]
+//!     state: State,
+//!     #[values(Event::Error, Event::Fatal)]
+//!     event: Event
+//! ) {
+//!     assert_eq!(State::Terminated, state.process(event))
+//! }
+//! ```
+//!
+//! This will generate a test for each combination of `state` and `event`.
+//!
+//! ## Magic Conversion
+//!
+//! If you need a value where its type implement `FromStr()` trait you
+//! can use a literal string to build it.
+//!
+//! ```
+//! # use rstest::rstest;
+//! # use std::net::SocketAddr;
+//! #[rstest]
+//! #[case("1.2.3.4:8080", 8080)]
+//! #[case("127.0.0.1:9000", 9000)]
+//! fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) {
+//!     assert_eq!(expected, addr.port());
+//! }
+//! ```
+//! You can use this feature also in value list and in fixture default value.
+
+#![cfg_attr(use_proc_macro_diagnostic, feature(proc_macro_diagnostic))]
+extern crate proc_macro;
+
+// Test utility module
+#[cfg(test)]
+pub(crate) mod test;
+#[cfg(test)]
+use rstest_reuse;
+
+#[macro_use]
+mod error;
+mod parse;
+mod refident;
+mod render;
+mod resolver;
+mod utils;
+
+use syn::{parse_macro_input, ItemFn};
+
+use crate::parse::{fixture::FixtureInfo, future::ReplaceFutureAttribute, rstest::RsTestInfo};
+use parse::ExtendWithFunctionAttrs;
+use quote::ToTokens;
+
+/// Define a fixture that you can use in all `rstest`'s test arguments. You should just mark your
+/// function as `#[fixture]` and then use it as a test's argument. Fixture functions can also
+/// use other fixtures.
+///
+/// Let's see a trivial example:
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn twenty_one() -> i32 { 21 }
+///
+/// #[fixture]
+/// fn two() -> i32 { 2 }
+///
+/// #[fixture]
+/// fn injected(twenty_one: i32, two: i32) -> i32 { twenty_one * two }
+///
+/// #[rstest]
+/// fn the_test(injected: i32) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+///
+/// If the fixture function is an [`async` function](#async) your fixture become an `async`
+/// fixture.
+///
+/// # Default values
+///
+/// If you need to define argument default value you can use `#[default(expression)]`
+/// argument's attribute:
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn injected(
+///     #[default(21)]
+///     twenty_one: i32,
+///     #[default(1 + 1)]
+///     two: i32
+/// ) -> i32 { twenty_one * two }
+///
+/// #[rstest]
+/// fn the_test(injected: i32) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+/// The `expression` could be any valid rust expression, even an `async` block if you need.
+/// Moreover, if the type implements `FromStr` trait you can use a literal string to build it.
+///
+/// ```
+/// # use rstest::*;
+/// # use std::net::SocketAddr;
+/// # struct DbConnection {}
+/// #[fixture]
+/// fn db_connection(
+///     #[default("127.0.0.1:9000")]
+///     addr: SocketAddr
+/// ) -> DbConnection {
+///     // create connection
+/// # DbConnection{}
+/// }
+/// ```
+///
+/// # Async
+///
+/// If you need you can write `async` fixtures to use in your `async` tests. Simply use `async`
+/// keyword for your function and the fixture become an `async` fixture.
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// async fn async_fixture() -> i32 { 42 }
+///
+///
+/// #[rstest]
+/// async fn the_test(#[future] async_fixture: i32) {
+///     assert_eq!(42, async_fixture.await)
+/// }
+/// ```
+/// The `#[future]` argument attribute helps to remove the `impl Future<Output = T>` boilerplate.
+/// In this case the macro expands it in:
+///
+/// ```
+/// # use rstest::*;
+/// # use std::future::Future;
+/// # #[fixture]
+/// # async fn async_fixture() -> i32 { 42 }
+/// #[rstest]
+/// async fn the_test(async_fixture: impl std::future::Future<Output = i32>) {
+///     assert_eq!(42, async_fixture.await)
+/// }
+/// ```
+/// If you need, you can use `#[future]` attribute also with an implicit lifetime reference
+/// because the macro will replace the implicit lifetime with an explicit one.
+///
+/// # Rename
+///
+/// Sometimes you want to have long and descriptive name for your fixture but you prefer to use a much
+/// shorter name for argument that represent it in your fixture or test. You can rename the fixture
+/// using `#[from(short_name)]` attribute like following example:
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn long_and_boring_descriptive_name() -> i32 { 42 }
+///
+/// #[rstest]
+/// fn the_test(#[from(long_and_boring_descriptive_name)] short: i32) {
+///     assert_eq!(42, short)
+/// }
+/// ```
+///
+/// # `#[once]` Fixture
+///
+/// Expecially in integration tests there are cases where you need a fixture that is called just once
+/// for every tests. `rstest` provides `#[once]` attribute for these cases.
+///
+/// If you mark your fixture with this attribute and `rstest` will compute a static reference to your
+/// fixture result and return this reference to all your tests that need this fixture.
+///
+/// In follow example all tests share the same reference to the `42` static value.
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// #[once]
+/// fn once_fixture() -> i32 { 42 }
+///
+/// // Take care!!! You need tu use a reference to fixture value
+///
+/// #[rstest]
+/// #[case(1)]
+/// #[case(2)]
+/// fn cases_tests(once_fixture: &i32, #[case] v: i32) {
+///     // Take care!!! You need tu use a reference to fixture value
+///     assert_eq!(&42, once_fixture)
+/// }
+///
+/// #[rstest]
+/// fn single(once_fixture: &i32) {
+///     assert_eq!(&42, once_fixture)
+/// }
+/// ```
+///
+/// There are some limitations when you use `#[once]` fixture. `rstest` forbid to use once fixture
+/// for:
+///
+/// - `async` function
+/// - Generic function (both with generic types or use `impl` trait)
+///
+/// Take care that the `#[once]` fixture value will **never dropped**.
+///
+/// # Partial Injection
+///
+/// You can also partialy inject fixture dependency using `#[with(v1, v2, ..)]` attribute:
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn base() -> i32 { 1 }
+///
+/// #[fixture]
+/// fn first(base: i32) -> i32 { 1 * base }
+///
+/// #[fixture]
+/// fn second(base: i32) -> i32 { 2 * base }
+///
+/// #[fixture]
+/// fn injected(first: i32, #[with(3)] second: i32) -> i32 { first * second }
+///
+/// #[rstest]
+/// fn the_test(injected: i32) {
+///     assert_eq!(-6, injected)
+/// }
+/// ```
+/// Note that injected value can be an arbitrary rust expression. `#[with(v1, ..., vn)]`
+/// attribute will inject `v1, ..., vn` expression as fixture arguments: all remaining arguments
+/// will be resolved as fixtures.
+///
+/// Sometimes the return type cannot be infered so you must define it: For the few times you may
+/// need to do it, you can use the `#[default(type)]`, `#[partial_n(type)]` function attribute
+/// to define it:
+///
+/// ```
+/// use rstest::*;
+/// # use std::fmt::Debug;
+///
+/// #[fixture]
+/// pub fn i() -> u32 {
+///     42
+/// }
+///
+/// #[fixture]
+/// pub fn j() -> i32 {
+///     -42
+/// }
+///
+/// #[fixture]
+/// #[default(impl Iterator<Item=(u32, i32)>)]
+/// #[partial_1(impl Iterator<Item=(I,i32)>)]
+/// pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
+///     std::iter::once((i, j))
+/// }
+///
+/// #[rstest]
+/// fn resolve_by_default(mut fx: impl Iterator<Item=(u32, i32)>) {
+///     assert_eq!((42, -42), fx.next().unwrap())
+/// }
+///
+/// #[rstest]
+/// fn resolve_partial(#[with(42.0)] mut fx: impl Iterator<Item=(f32, i32)>) {
+///     assert_eq!((42.0, -42), fx.next().unwrap())
+/// }
+/// ```
+/// `partial_i` is the fixture used when you inject the first `i` arguments in test call.
+///
+/// # Old _compact_ syntax
+///
+/// There is also a compact form for all previous features. This will mantained for a long time
+/// but for `fixture` I strongly recomand to migrate your code because you'll pay a little
+/// verbosity but get back a more readable code.
+///
+/// Follow the previous examples in old _compact_ syntax.
+///
+/// ## Default
+/// ```
+/// # use rstest::*;
+/// #[fixture(twenty_one=21, two=2)]
+/// fn injected(twenty_one: i32, two: i32) -> i32 { twenty_one * two }
+/// ```
+///
+/// ## Rename
+/// ```
+/// # use rstest::*;
+/// #[fixture]
+/// fn long_and_boring_descriptive_name() -> i32 { 42 }
+///
+/// #[rstest(long_and_boring_descriptive_name as short)]
+/// fn the_test(short: i32) {
+///     assert_eq!(42, short)
+/// }
+/// ```
+///
+/// ## Partial Injection
+/// ```
+/// # use rstest::*;
+/// # #[fixture]
+/// # fn base() -> i32 { 1 }
+/// #
+/// # #[fixture]
+/// # fn first(base: i32) -> i32 { 1 * base }
+/// #
+/// # #[fixture]
+/// # fn second(base: i32) -> i32 { 2 * base }
+/// #
+/// #[fixture(second(-3))]
+/// fn injected(first: i32, second: i32) -> i32 { first * second }
+/// ```
+/// ## Partial Type Injection
+/// ```
+/// # use rstest::*;
+/// # use std::fmt::Debug;
+/// #
+/// # #[fixture]
+/// # pub fn i() -> u32 {
+/// #     42
+/// # }
+/// #
+/// # #[fixture]
+/// # pub fn j() -> i32 {
+/// #     -42
+/// # }
+/// #
+/// #[fixture(::default<impl Iterator<Item=(u32, i32)>>::partial_1<impl Iterator<Item=(I,i32)>>)]
+/// pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
+///     std::iter::once((i, j))
+/// }
+/// ```
+
+#[proc_macro_attribute]
+pub fn fixture(
+    args: proc_macro::TokenStream,
+    input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    let mut info: FixtureInfo = parse_macro_input!(args as FixtureInfo);
+    let mut fixture = parse_macro_input!(input as ItemFn);
+
+    let replace_result = ReplaceFutureAttribute::replace(&mut fixture);
+    let extend_result = info.extend_with_function_attrs(&mut fixture);
+
+    let mut errors = error::fixture(&fixture, &info);
+
+    if let Err(attrs_errors) = replace_result {
+        attrs_errors.to_tokens(&mut errors);
+    }
+    if let Err(attrs_errors) = extend_result {
+        attrs_errors.to_tokens(&mut errors);
+    }
+
+    if errors.is_empty() {
+        render::fixture(fixture, info)
+    } else {
+        errors
+    }
+    .into()
+}
+
+/// The attribute that you should use for your tests. Your
+/// annotated function's arguments can be
+/// [injected](attr.rstest.html#injecting-fixtures) with
+/// [`[fixture]`](macro@fixture)s, provided by
+/// [parametrized cases](attr.rstest.html#test-parametrized-cases)
+/// or by [value lists](attr.rstest.html#values-lists).
+///
+/// `rstest` attribute can be applied to _any_ function and you can costumize its
+/// parameters by using function and arguments attributes.
+///
+/// Your test function can use generics, `impl` or `dyn` and like any kind of rust tests:
+///
+/// - return results
+/// - marked by `#[should_panic]` attribute
+///
+/// If the test function is an [`async` function](#async) `rstest` will run all tests as `async`
+/// tests. You can use it just with `async-std` and you should include `attributes` in
+/// `async-std`'s features.
+///
+/// In your test function you can:
+///
+/// - [injecting fixtures](#injecting-fixtures)
+/// - Generate [parametrized test cases](#test-parametrized-cases)
+/// - Generate tests for each combination of [value lists](#values-lists)
+///
+/// ## Injecting Fixtures
+///
+/// The simplest case is write a test that can be injected with
+/// [`[fixture]`](macro@fixture)s. You can just declare all used fixtures by passing
+/// them as a function's arguments. This can help your test to be neat
+/// and make your dependecy clear.
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn injected() -> i32 { 42 }
+///
+/// #[rstest]
+/// fn the_test(injected: i32) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+///
+/// [`[rstest]`](macro@rstest) procedural macro will desugar it to something that isn't
+/// so far from
+///
+/// ```
+/// #[test]
+/// fn the_test() {
+///     let injected=injected();
+///     assert_eq!(42, injected)
+/// }
+/// ```
+///
+/// If you want to use long and descriptive names for your fixture but prefer to use
+/// shorter names inside your tests you use rename feature described in
+/// [fixture rename](attr.fixture.html#rename):
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn long_and_boring_descriptive_name() -> i32 { 42 }
+///
+/// #[rstest]
+/// fn the_test(#[from(long_and_boring_descriptive_name)] short: i32) {
+///     assert_eq!(42, short)
+/// }
+/// ```
+///
+/// Sometimes is useful to have some parametes in your fixtures but your test would
+/// override the fixture's default values in some cases. Like in
+/// [fixture partial injection](attr.fixture.html#partial-injection) you use `#[with]`
+/// attribute to indicate some fixture's arguments also in `rstest`.
+///
+/// ```
+/// # struct User(String, u8);
+/// # impl User { fn name(&self) -> &str {&self.0} }
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn user(
+///     #[default("Alice")] name: impl AsRef<str>,
+///     #[default(22)] age: u8
+/// ) -> User { User(name.as_ref().to_owned(), age) }
+///
+/// #[rstest]
+/// fn check_user(#[with("Bob")] user: User) {
+///     assert_eq("Bob", user.name())
+/// }
+/// ```
+///
+/// ## Test Parametrized Cases
+///
+/// If you would execute your test for a set of input data cases
+/// you can define the arguments to use and the cases list. Let see
+/// the classical Fibonacci example. In this case we would give the
+/// `input` value and the `expected` result for a set of cases to test.
+///
+/// ```
+/// use rstest::rstest;
+///
+/// #[rstest]
+/// #[case(0, 0)]
+/// #[case(1, 1)]
+/// #[case(2, 1)]
+/// #[case(3, 2)]
+/// #[case(4, 3)]
+/// fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {
+///     assert_eq!(expected, fibonacci(input))
+/// }
+///
+/// fn fibonacci(input: u32) -> u32 {
+///     match input {
+///         0 => 0,
+///         1 => 1,
+///         n => fibonacci(n - 2) + fibonacci(n - 1)
+///     }
+/// }
+/// ```
+///
+/// `rstest` will produce 5 indipendent tests and not just one that
+/// check every case. Every test can fail indipendently and `cargo test`
+/// will give follow output:
+///
+/// ```text
+/// running 5 tests
+/// test fibonacci_test::case_1 ... ok
+/// test fibonacci_test::case_2 ... ok
+/// test fibonacci_test::case_3 ... ok
+/// test fibonacci_test::case_4 ... ok
+/// test fibonacci_test::case_5 ... ok
+///
+/// test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+/// ```
+///
+/// The cases input values can be arbitrary Rust expresions that return the
+/// argument type.
+///
+/// ```
+/// use rstest::rstest;
+///  
+/// fn sum(a: usize, b: usize) -> usize { a + b }
+///
+/// #[rstest]
+/// #[case("foo", 3)]
+/// #[case(String::from("foo"), 2 + 1)]
+/// #[case(format!("foo"), sum(2, 1))]
+/// fn test_len(#[case] s: impl AsRef<str>,#[case] len: usize) {
+///     assert_eq!(s.as_ref().len(), len);
+/// }
+/// ```
+///
+/// ### Magic Conversion
+///
+/// You can use the magic conversion feature every time you would define a variable
+/// where its type define `FromStr` trait: test will parse the string to build the value.
+///
+/// ```
+/// # use rstest::rstest;
+/// # use std::path::PathBuf;
+/// # fn count_words(path: PathBuf) -> usize {0}
+/// #[rstest]
+/// #[case("resources/empty", 0)]
+/// #[case("resources/divine_commedy", 101.698)]
+/// fn test_count_words(#[case] path: PathBuf, #[case] expected: usize) {
+///     assert_eq!(expected, count_words(path))
+/// }
+/// ```
+///
+/// ### Optional case description
+///
+/// Optionally you can give a _description_ to every case simple by follow `case`
+/// with `::my_case_description` where `my_case_description` should be a a valid
+/// Rust ident.
+///
+/// ```
+/// # use rstest::*;
+/// #[rstest]
+/// #[case::zero_base_case(0, 0)]
+/// #[case::one_base_case(1, 1)]
+/// #[case(2, 1)]
+/// #[case(3, 2)]
+/// fn fibonacci_test(#[case] input: u32,#[case] expected: u32) {
+///     assert_eq!(expected, fibonacci(input))
+/// }
+///
+/// # fn fibonacci(input: u32) -> u32 {
+/// #     match input {
+/// #         0 => 0,
+/// #         1 => 1,
+/// #         n => fibonacci(n - 2) + fibonacci(n - 1)
+/// #     }
+/// # }
+/// ```
+///
+/// Outuput will be
+/// ```text
+/// running 4 tests
+/// test fibonacci_test::case_1_zero_base_case ... ok
+/// test fibonacci_test::case_2_one_base_case ... ok
+/// test fibonacci_test::case_3 ... ok
+/// test fibonacci_test::case_4 ... ok
+///
+/// test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+/// ```
+///
+/// ### Use specific `case` attributes
+///
+/// Every function's attributes that preceding a `#[case]` attribute will
+/// be used in this test case and all function's attributes that follow the
+/// last `#[case]` attribute will mark all test cases.
+///
+/// This feature can be use to mark just some cases as `should_panic`
+/// and choose to have a fine grain on expected panic messages.
+///
+/// In follow example we run 3 tests where the first pass without any
+/// panic, in the second we catch a panic but we don't care about the message
+/// and in the third one we also check the panic message.
+///
+/// ```
+/// use rstest::rstest;
+///
+/// #[rstest]
+/// #[case::no_panic(0)]
+/// #[should_panic]
+/// #[case::panic(1)]
+/// #[should_panic(expected="expected")]
+/// #[case::panic_with_message(2)]
+/// fn attribute_per_case(#[case] val: i32) {
+///     match val {
+///         0 => assert!(true),
+///         1 => panic!("No catch"),
+///         2 => panic!("expected"),
+///         _ => unreachable!(),
+///     }
+/// }
+/// ```
+///
+/// Output:
+///
+/// ```text
+/// running 3 tests
+/// test attribute_per_case::case_1_no_panic ... ok
+/// test attribute_per_case::case_3_panic_with_message ... ok
+/// test attribute_per_case::case_2_panic ... ok
+///
+/// test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+/// ```
+///
+/// To mark all your tests as `#[should_panic]` use:
+///
+/// ```
+/// # use rstest::rstest;
+/// #[rstest]
+/// #[case(1)]
+/// #[case(2)]
+/// #[case(3)]
+/// #[should_panic]
+/// fn fail(#[case] v: u32) { assert_eq!(0, v) }
+/// ```
+///
+/// ## Values Lists
+///
+/// Another useful way to write a test and execute it for some values
+/// is to use the values list syntax. This syntax can be usefull both
+/// for a plain list and for testing all combination of input arguments.
+///
+/// ```
+/// # use rstest::*;
+/// # fn is_valid(input: &str) -> bool { true }
+///
+/// #[rstest]
+/// fn should_be_valid(
+///     #[values("Jhon", "alice", "My_Name", "Zigy_2001")]
+///     input: &str
+/// ) {
+///     assert!(is_valid(input))
+/// }
+/// ```
+///
+/// or
+///
+/// ```
+/// # use rstest::*;
+/// # fn valid_user(name: &str, age: u8) -> bool { true }
+///
+/// #[rstest]
+/// fn should_accept_all_corner_cases(
+///     #[values("J", "A", "A________________________________________21")]
+///     name: &str,
+///     #[values(14, 100)]
+///     age: u8
+/// ) {
+///     assert!(valid_user(name, age))
+/// }
+/// ```
+/// where `cargo test` output is
+///
+/// ```text
+/// running 6 tests
+/// test should_accept_all_corner_cases::name_1::age_1 ... ok
+/// test should_accept_all_corner_cases::name_3::age_1 ... ok
+/// test should_accept_all_corner_cases::name_3::age_2 ... ok
+/// test should_accept_all_corner_cases::name_2::age_1 ... ok
+/// test should_accept_all_corner_cases::name_2::age_2 ... ok
+/// test should_accept_all_corner_cases::name_1::age_2 ... ok
+///
+/// test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+/// ```
+///
+/// Also value list implements the magic conversion feature: every time the value type
+/// implements `FromStr` trait you can use a literal string to define it.
+///
+/// ## Use Parametrize definition in more tests
+///
+/// If you need to use a test list for more than one test you can use
+/// [`rstest_reuse`](https://crates.io/crates/rstest_reuse) crate.
+/// With this helper crate you can define a template and use it everywhere.
+///
+/// ```
+/// # use rstest::rstest;
+/// # use std::net::SocketAddr;
+/// #[rstest]
+/// fn given_port(#[values("1.2.3.4:8000", "4.3.2.1:8000", "127.0.0.1:8000")] addr: SocketAddr) {
+///     assert_eq(8000, addr.port())
+/// }
+/// ```
+///
+/// ```rust,ignore
+/// use rstest::rstest;
+/// use rstest_reuse::{self, *};
+///
+/// #[template]
+/// #[rstest]
+/// #[case(2, 2)]
+/// #[case(4/2, 2)]
+/// fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
+///
+/// #[apply(two_simple_cases)]
+/// fn it_works(#[case] a: u32,#[case] b: u32) {
+///     assert!(a == b);
+/// }
+/// ```
+///
+/// See [`rstest_reuse`](https://crates.io/crates/rstest_reuse) for more dettails.
+///
+/// ## Async
+///
+/// `rstest` provides out of the box `async` support. Just mark your
+/// test function as `async` and it'll use `#[async-std::test]` to
+/// annotate it. This feature can be really useful to build async
+/// parametric tests using a tidy syntax:
+///
+/// ```
+/// use rstest::*;
+/// # async fn async_sum(a: u32, b: u32) -> u32 { a + b }
+///
+/// #[rstest]
+/// #[case(5, 2, 3)]
+/// #[should_panic]
+/// #[case(42, 40, 1)]
+/// async fn my_async_test(#[case] expected: u32, #[case] a: u32, #[case] b: u32) {
+///     assert_eq!(expected, async_sum(a, b).await);
+/// }
+/// ```
+///
+/// Currently only `async-std` is supported out of the box. But if you need to use
+/// another runtime that provide it's own test attribute (i.e. `tokio::test` or
+/// `actix_rt::test`) you can use it in your `async` test like described in
+/// [Inject Test Attribute](attr.rstest.html#inject-test-attribute).
+///
+/// To use this feature, you need to enable `attributes` in the `async-std`
+/// features list in your `Cargo.toml`:
+///
+/// ```toml
+/// async-std = { version = "1.5", features = ["attributes"] }
+/// ```
+///
+/// If your test input is an async value (fixture or test parameter) you can use `#[future]`
+/// attribute to remove `impl Future<Output = T>` boilerplate and just use `T`:
+///
+/// ```
+/// use rstest::*;
+/// #[fixture]
+/// async fn base() -> u32 { 42 }
+///
+/// #[rstest]
+/// #[case(21, async { 2 })]
+/// #[case(6, async { 7 })]
+/// async fn my_async_test(#[future] base: u32, #[case] expected: u32, #[future] #[case] div: u32) {
+///     assert_eq!(expected, base.await / div.await);
+/// }
+/// ```
+///
+/// ## Inject Test Attribute
+///
+/// If you would like to use another `test` attribute for your test you can simply
+/// indicate it in your test function's attributes. For instance if you want
+/// to test some async function with use `actix_rt::test` attribute you can just write:
+///
+/// ```
+/// use rstest::*;
+/// use actix_rt;
+/// use std::future::Future;
+///
+/// #[rstest]
+/// #[case(2, async { 4 })]
+/// #[case(21, async { 42 })]
+/// #[actix_rt::test]
+/// async fn my_async_test(#[case] a: u32, #[case] #[future] result: u32) {
+///     assert_eq!(2 * a, result.await);
+/// }
+/// ```
+/// Just the attributes that ends with `test` (last path segment) can be injected:
+/// in this case the `#[actix_rt::test]` attribute will replace the standard `#[test]`
+/// attribute.
+///
+/// ## Putting all Together
+///
+/// All these features can be used together with a mixture of fixture variables,
+/// fixed cases and bunch of values. For instance, you might need two
+/// test cases which test for panics, one for a logged in user and one for a guest user.
+///
+/// ```rust
+/// # enum User { Guest, Logged, }
+/// # impl User { fn logged(_n: &str, _d: &str, _w: &str, _s: &str) -> Self { Self::Logged } }
+/// # struct Item {}
+/// # trait Repository { fn find_items(&self, user: &User, query: &str) -> Result<Vec<Item>, String> { Err("Invalid query error".to_owned()) } }
+/// # #[derive(Default)] struct InMemoryRepository {}
+/// # impl Repository for InMemoryRepository {}
+///
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn repository() -> InMemoryRepository {
+///     let mut r = InMemoryRepository::default();
+///     // fill repository with some data
+///     r
+/// }
+///
+/// #[fixture]
+/// fn alice() -> User {
+///     User::logged("Alice", "2001-10-04", "London", "UK")
+/// }
+///
+/// #[rstest]
+/// #[case::authed_user(alice())] // We can use `fixture` also as standard function
+/// #[case::guest(User::Guest)]   // We can give a name to every case : `guest` in this case
+/// #[should_panic(expected = "Invalid query error")] // We whould test a panic
+/// fn should_be_invalid_query_error(
+///     repository: impl Repository,
+///     #[case] user: User,
+///     #[values("     ", "^%$#@!", "....")]
+///     query: &str
+/// ) {
+///     repository.find_items(&user, query).unwrap();
+/// }
+/// ```
+///
+/// ## Trace Input Arguments
+///
+/// Sometimes can be very helpful to print all test's input arguments. To
+/// do it you can use the `#[trace]` function attribute that you can apply
+/// to all cases or just to some of them.
+///
+/// ```
+/// use rstest::*;
+///
+/// #[fixture]
+/// fn injected() -> i32 { 42 }
+///
+/// #[rstest]
+/// #[trace]
+/// fn the_test(injected: i32) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+///
+/// Will print an output like
+///
+/// ```bash
+/// Testing started at 14.12 ...
+/// ------------ TEST ARGUMENTS ------------
+/// injected = 42
+/// -------------- TEST START --------------
+///
+///
+/// Expected :42
+/// Actual   :43
+/// ```
+/// But
+/// ```
+/// # use rstest::*;
+/// #[rstest]
+/// #[case(1)]
+/// #[trace]
+/// #[case(2)]
+/// fn the_test(#[case] v: i32) {
+///     assert_eq!(0, v)
+/// }
+/// ```
+/// will trace just `case_2` input arguments.
+///
+/// If you want to trace input arguments but skip some of them that don't
+/// implement the `Debug` trait, you can also use the
+/// `#[notrace]` argument attribute to skip them:
+///
+/// ```
+/// # use rstest::*;
+/// # struct Xyz;
+/// # struct NoSense;
+/// #[rstest]
+/// #[trace]
+/// fn the_test(injected: i32, #[notrace] xyz: Xyz, #[notrace] have_no_sense: NoSense) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+/// # Old _compact_ syntax
+///
+/// `rstest` support also a syntax where all options and configuration can be write as
+/// `rstest` attribute arguments. This syntax is a little less verbose but make
+/// composition harder: for istance try to add some cases to a `rstest_reuse` template
+/// is really hard.
+///
+/// So we'll continue to maintain the old syntax for a long time but we strongly encourage
+/// to switch your test in the new form.
+///
+/// Anyway, here we recall this syntax and rewrite the previous example in the _compact_ form.
+///
+/// ```text
+/// rstest(
+///     arg_1,
+///     ...,
+///     arg_n[,]
+///     [::attribute_1[:: ... [::attribute_k]]]
+/// )
+/// ```
+/// Where:
+///
+/// - `arg_i` could be one of the follow
+///   - `ident` that match to one of function arguments for parametrized cases
+///   - `case[::description](v1, ..., vl)` a test case
+///   - `fixture(v1, ..., vl) [as argument_name]` where fixture is the injected
+/// fixture and argument_name (default use fixture) is one of function arguments
+/// that and `v1, ..., vl` is a partial list of fixture's arguments
+///   - `ident => [v1, ..., vl]` where `ident` is one of function arguments and
+/// `v1, ..., vl` is a list of values for ident
+/// - `attribute_j` a test attribute like `trace` or `notrace`
+///
+/// ## Fixture Arguments
+///
+/// ```
+/// # struct User(String, u8);
+/// # impl User { fn name(&self) -> &str {&self.0} }
+/// # use rstest::*;
+/// #
+/// # #[fixture]
+/// # fn user(
+/// #     #[default("Alice")] name: impl AsRef<str>,
+/// #     #[default(22)] age: u8
+/// # ) -> User { User(name.as_ref().to_owned(), age) }
+/// #
+/// #[rstest(user("Bob"))]
+/// fn check_user(user: User) {
+///     assert_eq("Bob", user.name())
+/// }
+/// ```
+///
+/// ## Fixture Rename
+/// ```
+/// # use rstest::*;
+/// #[fixture]
+/// fn long_and_boring_descriptive_name() -> i32 { 42 }
+///
+/// #[rstest(long_and_boring_descriptive_name as short)]
+/// fn the_test(short: i32) {
+///     assert_eq!(42, short)
+/// }
+/// ```
+///
+/// ## Parametrized
+///
+/// ```
+/// # use rstest::*;
+/// #[rstest(input, expected,
+///     case::zero_base_case(0, 0),
+///     case::one_base_case(1, 1),
+///     case(2, 1),
+///     case(3, 2),
+///     #[should_panic]
+///     case(4, 42)
+/// )]
+/// fn fibonacci_test(input: u32, expected: u32) {
+///     assert_eq!(expected, fibonacci(input))
+/// }
+///
+/// # fn fibonacci(input: u32) -> u32 {
+/// #     match input {
+/// #         0 => 0,
+/// #         1 => 1,
+/// #         n => fibonacci(n - 2) + fibonacci(n - 1)
+/// #     }
+/// # }
+/// ```
+///
+/// ## Values Lists
+///
+/// ```
+/// # use rstest::*;
+/// # fn is_valid(input: &str) -> bool { true }
+///
+/// #[rstest(
+///     input => ["Jhon", "alice", "My_Name", "Zigy_2001"]
+/// )]
+/// fn should_be_valid(input: &str) {
+///     assert!(is_valid(input))
+/// }
+/// ```
+///
+/// ## `trace` and `notrace`
+///
+/// ```
+/// # use rstest::*;
+/// # struct Xyz;
+/// # struct NoSense;
+/// #[rstest(::trace::notrace(xzy, have_no_sense))]
+/// fn the_test(injected: i32, xyz: Xyz, have_no_sense: NoSense) {
+///     assert_eq!(42, injected)
+/// }
+/// ```
+///
+#[proc_macro_attribute]
+pub fn rstest(
+    args: proc_macro::TokenStream,
+    input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    let mut test = parse_macro_input!(input as ItemFn);
+    let mut info = parse_macro_input!(args as RsTestInfo);
+
+    let replace_result = ReplaceFutureAttribute::replace(&mut test);
+    let extend_result = info.extend_with_function_attrs(&mut test);
+
+    let mut errors = error::rstest(&test, &info);
+
+    if let Err(attrs_errors) = replace_result {
+        attrs_errors.to_tokens(&mut errors);
+    }
+    if let Err(attrs_errors) = extend_result {
+        attrs_errors.to_tokens(&mut errors);
+    }
+
+    if errors.is_empty() {
+        if info.data.has_list_values() {
+            render::matrix(test, info)
+        } else if info.data.has_cases() {
+            render::parametrize(test, info)
+        } else {
+            render::single(test, info)
+        }
+    } else {
+        errors
+    }
+    .into()
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/expressions.rs b/third_party/rust/rstest/v0_12/crate/src/parse/expressions.rs
new file mode 100644
index 0000000..152662b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/expressions.rs
@@ -0,0 +1,28 @@
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    Expr, Token,
+};
+
+pub(crate) struct Expressions(Vec<Expr>);
+
+impl Expressions {
+    pub(crate) fn take(self) -> Vec<Expr> {
+        self.0
+    }
+}
+
+impl Parse for Expressions {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let values = input
+            .parse_terminated::<_, Token![,]>(Parse::parse)?
+            .into_iter()
+            .collect();
+        Ok(Self(values))
+    }
+}
+
+impl From<Expressions> for Vec<Expr> {
+    fn from(expressions: Expressions) -> Self {
+        expressions.0
+    }
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/fixture.rs b/third_party/rust/rstest/v0_12/crate/src/parse/fixture.rs
new file mode 100644
index 0000000..8825e65
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/fixture.rs
@@ -0,0 +1,728 @@
+/// `fixture`'s related data and parsing
+use syn::{
+    parse::{Parse, ParseStream},
+    parse_quote,
+    visit_mut::VisitMut,
+    Expr, FnArg, Ident, ItemFn, Token,
+};
+
+use super::{
+    extract_argument_attrs, extract_default_return_type, extract_defaults, extract_fixtures,
+    extract_partials_return_type, parse_vector_trailing_till_double_comma, Attributes,
+    ExtendWithFunctionAttrs, Fixture,
+};
+use crate::{
+    error::ErrorsVec,
+    parse::extract_once,
+    refident::{MaybeIdent, RefIdent},
+    utils::attr_is,
+};
+use crate::{parse::Attribute, utils::attr_in};
+use proc_macro2::TokenStream;
+use quote::{format_ident, ToTokens};
+
+#[derive(PartialEq, Debug, Default)]
+pub(crate) struct FixtureInfo {
+    pub(crate) data: FixtureData,
+    pub(crate) attributes: FixtureModifiers,
+}
+
+impl Parse for FixtureModifiers {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(input.parse::<Attributes>()?.into())
+    }
+}
+
+impl Parse for FixtureInfo {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(if input.is_empty() {
+            Default::default()
+        } else {
+            Self {
+                data: input.parse()?,
+                attributes: input
+                    .parse::<Token![::]>()
+                    .or_else(|_| Ok(Default::default()))
+                    .and_then(|_| input.parse())?,
+            }
+        })
+    }
+}
+
+impl ExtendWithFunctionAttrs for FixtureInfo {
+    fn extend_with_function_attrs(
+        &mut self,
+        item_fn: &mut ItemFn,
+    ) -> std::result::Result<(), ErrorsVec> {
+        let composed_tuple!(
+            fixtures,
+            defaults,
+            default_return_type,
+            partials_return_type,
+            once
+        ) = merge_errors!(
+            extract_fixtures(item_fn),
+            extract_defaults(item_fn),
+            extract_default_return_type(item_fn),
+            extract_partials_return_type(item_fn),
+            extract_once(item_fn)
+        )?;
+        self.data.items.extend(
+            fixtures
+                .into_iter()
+                .map(|f| f.into())
+                .chain(defaults.into_iter().map(|d| d.into())),
+        );
+        if let Some(return_type) = default_return_type {
+            self.attributes.set_default_return_type(return_type);
+        }
+        for (id, return_type) in partials_return_type {
+            self.attributes.set_partial_return_type(id, return_type);
+        }
+        if let Some(ident) = once {
+            self.attributes.set_once(ident)
+        };
+        Ok(())
+    }
+}
+
+fn parse_attribute_args_just_once<'a, T: Parse>(
+    attributes: impl Iterator<Item = &'a syn::Attribute>,
+    name: &str,
+) -> (Option<T>, Vec<syn::Error>) {
+    let mut errors = Vec::new();
+    let val = attributes
+        .filter(|&a| attr_is(a, name))
+        .map(|a| (a, a.parse_args::<T>()))
+        .fold(None, |first, (a, res)| match (first, res) {
+            (None, Ok(parsed)) => Some(parsed),
+            (first, Err(err)) => {
+                errors.push(err);
+                first
+            }
+            (first, _) => {
+                errors.push(syn::Error::new_spanned(
+                    a,
+                    format!(
+                        "You cannot use '{}' attribute more than once for the same argument",
+                        name
+                    ),
+                ));
+                first
+            }
+        });
+    (val, errors)
+}
+
+/// Simple struct used to visit function attributes and extract Fixtures and
+/// eventualy parsing errors
+#[derive(Default)]
+pub(crate) struct FixturesFunctionExtractor(pub(crate) Vec<Fixture>, pub(crate) Vec<syn::Error>);
+
+impl VisitMut for FixturesFunctionExtractor {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        if let FnArg::Typed(ref mut arg) = node {
+            let name = match arg.pat.as_ref() {
+                syn::Pat::Ident(ident) => ident.ident.clone(),
+                _ => return,
+            };
+            let (extracted, remain): (Vec<_>, Vec<_>) = std::mem::take(&mut arg.attrs)
+                .into_iter()
+                .partition(|attr| attr_in(attr, &["with", "from"]));
+            arg.attrs = remain;
+
+            let (pos, errors) = parse_attribute_args_just_once(extracted.iter(), "with");
+            self.1.extend(errors.into_iter());
+            let (resolve, errors) = parse_attribute_args_just_once(extracted.iter(), "from");
+            self.1.extend(errors.into_iter());
+            if pos.is_some() || resolve.is_some() {
+                self.0
+                    .push(Fixture::new(name, resolve, pos.unwrap_or_default()))
+            }
+        }
+    }
+}
+
+/// Simple struct used to visit function attributes and extract fixture default values info and
+/// eventualy parsing errors
+#[derive(Default)]
+pub(crate) struct DefaultsFunctionExtractor(
+    pub(crate) Vec<ArgumentValue>,
+    pub(crate) Vec<syn::Error>,
+);
+
+impl VisitMut for DefaultsFunctionExtractor {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        for r in extract_argument_attrs(
+            node,
+            |a| attr_is(a, "default"),
+            |a, name| {
+                a.parse_args::<Expr>()
+                    .map(|e| ArgumentValue::new(name.clone(), e))
+            },
+        ) {
+            match r {
+                Ok(value) => self.0.push(value),
+                Err(err) => self.1.push(err),
+            }
+        }
+    }
+}
+
+#[derive(PartialEq, Debug, Default)]
+pub(crate) struct FixtureData {
+    pub items: Vec<FixtureItem>,
+}
+
+impl FixtureData {
+    pub(crate) fn fixtures(&self) -> impl Iterator<Item = &Fixture> {
+        self.items.iter().filter_map(|f| match f {
+            FixtureItem::Fixture(ref fixture) => Some(fixture),
+            _ => None,
+        })
+    }
+
+    pub(crate) fn values(&self) -> impl Iterator<Item = &ArgumentValue> {
+        self.items.iter().filter_map(|f| match f {
+            FixtureItem::ArgumentValue(ref value) => Some(value.as_ref()),
+            _ => None,
+        })
+    }
+}
+
+impl Parse for FixtureData {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek(Token![::]) {
+            Ok(Default::default())
+        } else {
+            Ok(Self {
+                items: parse_vector_trailing_till_double_comma::<_, Token![,]>(input)?,
+            })
+        }
+    }
+}
+
+#[derive(PartialEq, Debug)]
+pub(crate) struct ArgumentValue {
+    pub name: Ident,
+    pub expr: Expr,
+}
+
+impl ArgumentValue {
+    pub(crate) fn new(name: Ident, expr: Expr) -> Self {
+        Self { name, expr }
+    }
+}
+
+#[derive(PartialEq, Debug)]
+pub(crate) enum FixtureItem {
+    Fixture(Fixture),
+    ArgumentValue(Box<ArgumentValue>),
+}
+
+impl From<Fixture> for FixtureItem {
+    fn from(f: Fixture) -> Self {
+        FixtureItem::Fixture(f)
+    }
+}
+
+impl Parse for FixtureItem {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek2(Token![=]) {
+            input.parse::<ArgumentValue>().map(|v| v.into())
+        } else {
+            input.parse::<Fixture>().map(|v| v.into())
+        }
+    }
+}
+
+impl RefIdent for FixtureItem {
+    fn ident(&self) -> &Ident {
+        match self {
+            FixtureItem::Fixture(Fixture { ref name, .. }) => name,
+            FixtureItem::ArgumentValue(ref av) => &av.name,
+        }
+    }
+}
+
+impl ToTokens for FixtureItem {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.ident().to_tokens(tokens)
+    }
+}
+
+impl From<ArgumentValue> for FixtureItem {
+    fn from(av: ArgumentValue) -> Self {
+        FixtureItem::ArgumentValue(Box::new(av))
+    }
+}
+
+impl Parse for ArgumentValue {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let name = input.parse()?;
+        let _eq: Token![=] = input.parse()?;
+        let expr = input.parse()?;
+        Ok(ArgumentValue::new(name, expr))
+    }
+}
+
+wrap_attributes!(FixtureModifiers);
+
+impl FixtureModifiers {
+    pub(crate) const DEFAULT_RET_ATTR: &'static str = "default";
+    pub(crate) const PARTIAL_RET_ATTR: &'static str = "partial_";
+
+    pub(crate) fn extract_default_type(&self) -> Option<syn::ReturnType> {
+        self.extract_type(Self::DEFAULT_RET_ATTR)
+    }
+
+    pub(crate) fn extract_partial_type(&self, pos: usize) -> Option<syn::ReturnType> {
+        self.extract_type(&format!("{}{}", Self::PARTIAL_RET_ATTR, pos))
+    }
+
+    pub(crate) fn set_default_return_type(&mut self, return_type: syn::Type) {
+        self.inner.attributes.push(Attribute::Type(
+            format_ident!("{}", Self::DEFAULT_RET_ATTR),
+            Box::new(return_type),
+        ))
+    }
+
+    pub(crate) fn set_partial_return_type(&mut self, id: usize, return_type: syn::Type) {
+        self.inner.attributes.push(Attribute::Type(
+            format_ident!("{}{}", Self::PARTIAL_RET_ATTR, id),
+            Box::new(return_type),
+        ))
+    }
+
+    pub(crate) fn set_once(&mut self, once: syn::Ident) {
+        self.inner.attributes.push(Attribute::Attr(once))
+    }
+
+    pub(crate) fn get_once(&self) -> Option<&Ident> {
+        self.iter()
+            .find(|&a| a == &Attribute::Attr(format_ident!("once")))
+            .and_then(|a| a.maybe_ident())
+    }
+
+    pub(crate) fn is_once(&self) -> bool {
+        self.get_once().is_some()
+    }
+
+    fn extract_type(&self, attr_name: &str) -> Option<syn::ReturnType> {
+        self.iter()
+            .filter_map(|m| match m {
+                Attribute::Type(name, t) if name == attr_name => Some(parse_quote! { -> #t}),
+                _ => None,
+            })
+            .next()
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::test::{assert_eq, *};
+
+    mod parse {
+        use super::{assert_eq, *};
+        use mytest::rstest;
+
+        fn parse_fixture<S: AsRef<str>>(fixture_data: S) -> FixtureInfo {
+            parse_meta(fixture_data)
+        }
+
+        #[test]
+        fn happy_path() {
+            let data = parse_fixture(
+                r#"my_fixture(42, "other"), other(vec![42]), value=42, other_value=vec![1.0]
+                    :: trace :: no_trace(some)"#,
+            );
+
+            let expected = FixtureInfo {
+                data: vec![
+                    fixture("my_fixture", &["42", r#""other""#]).into(),
+                    fixture("other", &["vec![42]"]).into(),
+                    arg_value("value", "42").into(),
+                    arg_value("other_value", "vec![1.0]").into(),
+                ]
+                .into(),
+                attributes: Attributes {
+                    attributes: vec![
+                        Attribute::attr("trace"),
+                        Attribute::tagged("no_trace", vec!["some"]),
+                    ],
+                }
+                .into(),
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        #[test]
+        fn some_literals() {
+            let args_expressions = literal_expressions_str();
+            let fixture = parse_fixture(&format!("my_fixture({})", args_expressions.join(", ")));
+            let args = fixture.data.fixtures().next().unwrap().positional.clone();
+
+            assert_eq!(to_args!(args_expressions), args.0);
+        }
+
+        #[test]
+        fn empty_fixtures() {
+            let data = parse_fixture(r#"::trace::no_trace(some)"#);
+
+            let expected = FixtureInfo {
+                attributes: Attributes {
+                    attributes: vec![
+                        Attribute::attr("trace"),
+                        Attribute::tagged("no_trace", vec!["some"]),
+                    ],
+                }
+                .into(),
+                ..Default::default()
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        #[test]
+        fn empty_attributes() {
+            let data = parse_fixture(r#"my_fixture(42, "other")"#);
+
+            let expected = FixtureInfo {
+                data: vec![fixture("my_fixture", &["42", r#""other""#]).into()].into(),
+                ..Default::default()
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        #[rstest]
+        #[case("first(42),", 1)]
+        #[case("first(42), second=42,", 2)]
+        #[case(r#"fixture(42, "other"), :: trace"#, 1)]
+        #[case(r#"second=42, fixture(42, "other"), :: trace"#, 2)]
+        fn should_accept_trailing_comma(#[case] input: &str, #[case] expected: usize) {
+            let info: FixtureInfo = input.ast();
+
+            assert_eq!(
+                expected,
+                info.data.fixtures().count() + info.data.values().count()
+            );
+        }
+    }
+}
+
+#[cfg(test)]
+mod extend {
+    use super::*;
+    use crate::test::{assert_eq, *};
+    use syn::ItemFn;
+
+    mod should {
+        use super::{assert_eq, *};
+
+        #[test]
+        fn use_with_attributes() {
+            let to_parse = r#"
+                fn my_fix(#[with(2)] f1: &str, #[with(vec![1,2], "s")] f2: u32) {}
+            "#;
+
+            let mut item_fn: ItemFn = to_parse.ast();
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            let expected = FixtureInfo {
+                data: vec![
+                    fixture("f1", &["2"]).into(),
+                    fixture("f2", &["vec![1,2]", r#""s""#]).into(),
+                ]
+                .into(),
+                ..Default::default()
+            };
+
+            assert!(!format!("{:?}", item_fn).contains("with"));
+            assert_eq!(expected, info);
+        }
+
+        #[test]
+        fn rename_with_attributes() {
+            let mut item_fn = r#"
+                    fn test_fn(
+                        #[from(long_fixture_name)] 
+                        #[with(42, "other")] short: u32, 
+                        #[from(simple)]
+                        s: &str,
+                        no_change: i32) {
+                    }
+                    "#
+            .ast();
+
+            let expected = FixtureInfo {
+                data: vec![
+                    fixture("short", &["42", r#""other""#])
+                        .with_resolve("long_fixture_name")
+                        .into(),
+                    fixture("s", &[]).with_resolve("simple").into(),
+                ]
+                .into(),
+                ..Default::default()
+            };
+
+            let mut data = FixtureInfo::default();
+            data.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            assert_eq!(expected, data);
+        }
+
+        #[test]
+        fn use_default_values_attributes() {
+            let to_parse = r#"
+                fn my_fix(#[default(2)] f1: &str, #[default((vec![1,2], "s"))] f2: (Vec<u32>, &str)) {}
+            "#;
+
+            let mut item_fn: ItemFn = to_parse.ast();
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            let expected = FixtureInfo {
+                data: vec![
+                    arg_value("f1", "2").into(),
+                    arg_value("f2", r#"(vec![1,2], "s")"#).into(),
+                ]
+                .into(),
+                ..Default::default()
+            };
+
+            assert!(!format!("{:?}", item_fn).contains("default"));
+            assert_eq!(expected, info);
+        }
+
+        #[test]
+        fn find_default_return_type() {
+            let mut item_fn: ItemFn = r#"
+                #[simple]
+                #[first(comp)]
+                #[second::default]
+                #[default(impl Iterator<Item=(u32, i32)>)]
+                #[last::more]
+                fn my_fix<I, J>(f1: I, f2: J) -> impl Iterator<Item=(I, J)> {}
+            "#
+            .ast();
+
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            assert_eq!(
+                info.attributes.extract_default_type(),
+                Some(parse_quote! { -> impl Iterator<Item=(u32, i32)> })
+            );
+            assert_eq!(
+                attrs("#[simple]#[first(comp)]#[second::default]#[last::more]"),
+                item_fn.attrs
+            );
+        }
+
+        #[test]
+        fn find_partials_return_type() {
+            let mut item_fn: ItemFn = r#"
+                #[simple]
+                #[first(comp)]
+                #[second::default]
+                #[partial_1(impl Iterator<Item=(u32, J, K)>)]
+                #[partial_2(impl Iterator<Item=(u32, i32, K)>)]
+                #[last::more]
+                fn my_fix<I, J, K>(f1: I, f2: J, f3: K) -> impl Iterator<Item=(I, J, K)> {}
+            "#
+            .ast();
+
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            assert_eq!(
+                info.attributes.extract_partial_type(1),
+                Some(parse_quote! { -> impl Iterator<Item=(u32, J, K)> })
+            );
+            assert_eq!(
+                info.attributes.extract_partial_type(2),
+                Some(parse_quote! { -> impl Iterator<Item=(u32, i32, K)> })
+            );
+            assert_eq!(
+                attrs("#[simple]#[first(comp)]#[second::default]#[last::more]"),
+                item_fn.attrs
+            );
+        }
+
+        #[test]
+        fn find_once_attribute() {
+            let mut item_fn: ItemFn = r#"
+                #[simple]
+                #[first(comp)]
+                #[second::default]
+                #[once]
+                #[last::more]
+                fn my_fix<I, J, K>(f1: I, f2: J, f3: K) -> impl Iterator<Item=(I, J, K)> {}
+            "#
+            .ast();
+
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            assert!(info.attributes.is_once());
+        }
+
+        #[test]
+        fn no_once_attribute() {
+            let mut item_fn: ItemFn = r#"
+                fn my_fix<I, J, K>(f1: I, f2: J, f3: K) -> impl Iterator<Item=(I, J, K)> {}
+            "#
+            .ast();
+
+            let mut info = FixtureInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+            assert!(!info.attributes.is_once());
+        }
+
+        mod raise_error {
+            use super::{assert_eq, *};
+            use rstest_test::assert_in;
+
+            #[test]
+            fn for_invalid_expressions() {
+                let mut item_fn: ItemFn = r#"
+                fn my_fix(#[with(valid)] f1: &str, #[with(with(,.,))] f2: u32, #[with(with(use))] f3: u32) {}
+                "#
+                .ast();
+
+                let errors = FixtureInfo::default()
+                    .extend_with_function_attrs(&mut item_fn)
+                    .unwrap_err();
+
+                assert_eq!(2, errors.len());
+            }
+
+            #[test]
+            fn for_invalid_default_type() {
+                let mut item_fn: ItemFn = r#"
+                    #[default(no<valid::>type)]
+                    fn my_fix<I>() -> I {}
+                "#
+                .ast();
+
+                let errors = FixtureInfo::default()
+                    .extend_with_function_attrs(&mut item_fn)
+                    .unwrap_err();
+
+                assert_eq!(1, errors.len());
+            }
+
+            #[test]
+            fn with_used_more_than_once() {
+                let mut item_fn: ItemFn = r#"
+                    fn my_fix(#[with(1)] #[with(2)] fixture1: &str, #[with(1)] #[with(2)] #[with(3)] fixture2: &str) {}
+                "#
+                .ast();
+
+                let errors = FixtureInfo::default()
+                    .extend_with_function_attrs(&mut item_fn)
+                    .err()
+                    .unwrap_or_default();
+
+                assert_eq!(3, errors.len());
+            }
+
+            #[test]
+            fn from_used_more_than_once() {
+                let mut item_fn: ItemFn = r#"
+                    fn my_fix(#[from(a)] #[from(b)] fixture1: &str, #[from(c)] #[from(d)] #[from(e)] fixture2: &str) {}
+                "#
+                .ast();
+
+                let errors = FixtureInfo::default()
+                    .extend_with_function_attrs(&mut item_fn)
+                    .err()
+                    .unwrap_or_default();
+
+                assert_eq!(3, errors.len());
+            }
+
+            #[test]
+            fn if_once_is_defined_more_than_once() {
+                let mut item_fn: ItemFn = r#"
+                    #[once]
+                    #[once]
+                    fn my_fix<I>() -> I {}
+                    "#
+                .ast();
+
+                let mut info = FixtureInfo::default();
+
+                let error = info.extend_with_function_attrs(&mut item_fn).unwrap_err();
+
+                assert_in!(
+                    format!("{:?}", error).to_lowercase(),
+                    "cannot use #[once] more than once"
+                );
+            }
+
+            #[test]
+            fn if_default_is_defined_more_than_once() {
+                let mut item_fn: ItemFn = r#"
+                    #[default(u32)]
+                    #[default(u32)]
+                    fn my_fix<I>() -> I {}
+                    "#
+                .ast();
+
+                let mut info = FixtureInfo::default();
+
+                let error = info.extend_with_function_attrs(&mut item_fn).unwrap_err();
+
+                assert_in!(
+                    format!("{:?}", error).to_lowercase(),
+                    "cannot use default more than once"
+                );
+            }
+
+            #[test]
+            fn for_invalid_partial_type() {
+                let mut item_fn: ItemFn = r#"
+                    #[partial_1(no<valid::>type)]
+                    fn my_fix<I>(x: I, y: u32) -> I {}
+                "#
+                .ast();
+
+                let errors = FixtureInfo::default()
+                    .extend_with_function_attrs(&mut item_fn)
+                    .unwrap_err();
+
+                assert_eq!(1, errors.len());
+            }
+
+            #[test]
+            fn if_partial_is_not_correct() {
+                let mut item_fn: ItemFn = r#"
+                    #[partial_not_a_number(u32)]
+                    fn my_fix<I, J>(f1: I, f2: &str) -> I {}
+                    "#
+                .ast();
+
+                let mut info = FixtureInfo::default();
+
+                let error = info.extend_with_function_attrs(&mut item_fn).unwrap_err();
+
+                assert_in!(
+                    format!("{:?}", error).to_lowercase(),
+                    "invalid partial syntax"
+                );
+            }
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/future.rs b/third_party/rust/rstest/v0_12/crate/src/parse/future.rs
new file mode 100644
index 0000000..08a63408
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/future.rs
@@ -0,0 +1,186 @@
+use quote::{format_ident, ToTokens};
+use syn::{parse_quote, visit_mut::VisitMut, FnArg, ItemFn, Lifetime};
+
+use crate::{error::ErrorsVec, refident::MaybeIdent, utils::attr_is};
+
+#[derive(Default)]
+pub(crate) struct ReplaceFutureAttribute {
+    lifetimes: Vec<Lifetime>,
+    errors: Vec<syn::Error>,
+}
+
+fn extend_generics_with_lifetimes<'a, 'b>(
+    generics: impl Iterator<Item = &'a syn::GenericParam>,
+    lifetimes: impl Iterator<Item = &'b syn::Lifetime>,
+) -> syn::Generics {
+    let all = lifetimes
+        .map(|lt| lt as &dyn ToTokens)
+        .chain(generics.map(|gp| gp as &dyn ToTokens));
+    parse_quote! {
+                <#(#all),*>
+    }
+}
+
+impl ReplaceFutureAttribute {
+    pub(crate) fn replace(item_fn: &mut ItemFn) -> Result<(), ErrorsVec> {
+        let mut visitor = Self::default();
+        visitor.visit_item_fn_mut(item_fn);
+        if !visitor.lifetimes.is_empty() {
+            item_fn.sig.generics = extend_generics_with_lifetimes(
+                item_fn.sig.generics.params.iter(),
+                visitor.lifetimes.iter(),
+            );
+        }
+        if visitor.errors.is_empty() {
+            Ok(())
+        } else {
+            Err(visitor.errors.into())
+        }
+    }
+}
+
+fn extract_arg_attributes(
+    node: &mut syn::PatType,
+    predicate: fn(a: &syn::Attribute) -> bool,
+) -> Vec<syn::Attribute> {
+    let attrs = std::mem::take(&mut node.attrs);
+    let (extracted, attrs): (Vec<_>, Vec<_>) = attrs.into_iter().partition(predicate);
+    node.attrs = attrs;
+    extracted
+}
+
+impl VisitMut for ReplaceFutureAttribute {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        let ident = node.maybe_ident().cloned();
+        match node {
+            FnArg::Typed(t) => {
+                let futures = extract_arg_attributes(t, |a| attr_is(a, "future"));
+                if futures.is_empty() {
+                    return;
+                } else if futures.len() > 1 {
+                    self.errors.extend(futures.iter().skip(1).map(|attr| {
+                        syn::Error::new_spanned(
+                            attr.into_token_stream(),
+                            "Cannot use #[future] more than once.".to_owned(),
+                        )
+                    }));
+                    return;
+                }
+                let ty = &mut t.ty;
+                use syn::Type::*;
+                match ty.as_ref() {
+                    Group(_) | ImplTrait(_) | Infer(_) | Macro(_) | Never(_) | Slice(_)
+                    | TraitObject(_) | Verbatim(_) => {
+                        self.errors.push(syn::Error::new_spanned(
+                            ty.into_token_stream(),
+                            "This type cannot used to generete impl Future.".to_owned(),
+                        ));
+                        return;
+                    }
+                    _ => {}
+                };
+                if let Reference(tr) = ty.as_mut() {
+                    let ident = ident.unwrap();
+                    if tr.lifetime.is_none() {
+                        let lifetime = syn::Lifetime {
+                            apostrophe: ident.span(),
+                            ident: format_ident!("_{}", ident),
+                        };
+                        self.lifetimes.push(lifetime.clone());
+                        tr.lifetime = lifetime.into();
+                    }
+                }
+
+                t.ty = parse_quote! {
+                    impl std::future::Future<Output = #ty>
+                }
+            }
+            FnArg::Receiver(_) => {}
+        }
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::test::{assert_eq, *};
+    use mytest::rstest;
+    use rstest_test::assert_in;
+
+    #[rstest]
+    #[case("fn simple(a: u32) {}")]
+    #[case("fn more(a: u32, b: &str) {}")]
+    #[case("fn gen<S: AsRef<str>>(a: u32, b: S) {}")]
+    #[case("fn attr(#[case] a: u32, #[values(1,2)] b: i32) {}")]
+    fn not_change_anything_if_no_future_attribute_found(#[case] item_fn: &str) {
+        let mut item_fn: ItemFn = item_fn.ast();
+        let orig = item_fn.clone();
+
+        ReplaceFutureAttribute::replace(&mut item_fn).unwrap();
+
+        assert_eq!(orig, item_fn)
+    }
+
+    #[rstest]
+    #[case::simple(
+        "fn f(#[future] a: u32) {}",
+        "fn f(a: impl std::future::Future<Output = u32>) {}"
+    )]
+    #[case::more_than_one(
+        "fn f(#[future] a: u32, #[future] b: String, #[future] c: std::collection::HashMap<usize, String>) {}",
+        r#"fn f(a: impl std::future::Future<Output = u32>, 
+                b: impl std::future::Future<Output = String>, 
+                c: impl std::future::Future<Output = std::collection::HashMap<usize, String>>) {}"#,
+    )]
+    #[case::just_one(
+        "fn f(a: u32, #[future] b: String) {}",
+        r#"fn f(a: u32, 
+                b: impl std::future::Future<Output = String>) {}"#
+    )]
+    #[case::generics(
+        "fn f<S: AsRef<str>>(#[future] a: S) {}",
+        "fn f<S: AsRef<str>>(a: impl std::future::Future<Output = S>) {}"
+    )]
+    fn replace_basic_type(#[case] item_fn: &str, #[case] expected: &str) {
+        let mut item_fn: ItemFn = item_fn.ast();
+        let expected: ItemFn = expected.ast();
+
+        ReplaceFutureAttribute::replace(&mut item_fn).unwrap();
+
+        assert_eq!(expected, item_fn)
+    }
+
+    #[rstest]
+    #[case::base(
+        "fn f(#[future] ident_name: &u32) {}",
+        "fn f<'_ident_name>(ident_name: impl std::future::Future<Output = &'_ident_name u32>) {}"
+    )]
+    #[case::lifetime_already_exists(
+        "fn f<'b>(#[future] a: &'b u32) {}",
+        "fn f<'b>(a: impl std::future::Future<Output = &'b u32>) {}"
+    )]
+    #[case::some_other_generics(
+        "fn f<'b, IT: Iterator<Item=String + 'b>>(#[future] a: &u32, it: IT) {}",
+        "fn f<'_a, 'b, IT: Iterator<Item=String + 'b>>(a: impl std::future::Future<Output = &'_a u32>, it: IT) {}"
+    )]
+    fn replace_reference_type(#[case] item_fn: &str, #[case] expected: &str) {
+        let mut item_fn: ItemFn = item_fn.ast();
+        let expected: ItemFn = expected.ast();
+
+        ReplaceFutureAttribute::replace(&mut item_fn).unwrap();
+
+        assert_eq!(expected, item_fn)
+    }
+
+    #[rstest]
+    #[case::no_more_than_one("fn f(#[future] #[future] a: u32) {}", "more than once")]
+    #[case::no_impl("fn f(#[future] a: impl AsRef<str>) {}", "generete impl Future")]
+    #[case::no_slice("fn f(#[future] a: [i32]) {}", "generete impl Future")]
+    fn raise_error(#[case] item_fn: &str, #[case] message: &str) {
+        let mut item_fn: ItemFn = item_fn.ast();
+
+        let err = ReplaceFutureAttribute::replace(&mut item_fn).unwrap_err();
+
+        assert_in!(format!("{:?}", err), message);
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/macros.rs b/third_party/rust/rstest/v0_12/crate/src/parse/macros.rs
new file mode 100644
index 0000000..beb47cf
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/macros.rs
@@ -0,0 +1,27 @@
+macro_rules! wrap_attributes {
+    ($ident:ident) => {
+        #[derive(Default, Debug, PartialEq, Clone)]
+        pub(crate) struct $ident {
+            inner: Attributes,
+        }
+
+        impl From<Attributes> for $ident {
+            fn from(inner: Attributes) -> Self {
+                $ident { inner }
+            }
+        }
+
+        impl $ident {
+            fn iter(&self) -> impl Iterator<Item = &Attribute> {
+                self.inner.attributes.iter()
+            }
+        }
+
+        impl $ident {
+            #[allow(dead_code)]
+            pub(crate) fn append(&mut self, attr: Attribute) {
+                self.inner.attributes.push(attr)
+            }
+        }
+    };
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/mod.rs b/third_party/rust/rstest/v0_12/crate/src/parse/mod.rs
new file mode 100644
index 0000000..61168fb
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/mod.rs
@@ -0,0 +1,629 @@
+use proc_macro2::TokenStream;
+use syn::{
+    parse::{Parse, ParseStream},
+    parse_quote,
+    punctuated::Punctuated,
+    token::{self, Paren},
+    visit_mut::VisitMut,
+    FnArg, Ident, ItemFn, Token,
+};
+
+use crate::{
+    error::ErrorsVec,
+    refident::{MaybeIdent, RefIdent},
+    utils::{attr_is, attr_starts_with},
+};
+use fixture::{
+    ArgumentValue, DefaultsFunctionExtractor, FixtureModifiers, FixturesFunctionExtractor,
+};
+use quote::ToTokens;
+use testcase::TestCase;
+
+use self::{expressions::Expressions, vlist::ValueList};
+
+// To use the macros this should be the first one module
+#[macro_use]
+pub(crate) mod macros;
+
+pub(crate) mod expressions;
+pub(crate) mod fixture;
+pub(crate) mod future;
+pub(crate) mod rstest;
+pub(crate) mod testcase;
+pub(crate) mod vlist;
+
+pub(crate) trait ExtendWithFunctionAttrs {
+    fn extend_with_function_attrs(
+        &mut self,
+        item_fn: &mut ItemFn,
+    ) -> std::result::Result<(), ErrorsVec>;
+}
+
+#[derive(Default, Debug, PartialEq, Clone)]
+pub(crate) struct Attributes {
+    pub(crate) attributes: Vec<Attribute>,
+}
+
+impl Parse for Attributes {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let vars = Punctuated::<Attribute, Token![::]>::parse_terminated(input)?;
+        Ok(Attributes {
+            attributes: vars.into_iter().collect(),
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub(crate) enum Attribute {
+    Attr(Ident),
+    Tagged(Ident, Vec<Ident>),
+    Type(Ident, Box<syn::Type>),
+}
+
+impl Parse for Attribute {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek2(Token![<]) {
+            let tag = input.parse()?;
+            let _open = input.parse::<Token![<]>()?;
+            let inner = input.parse()?;
+            let _close = input.parse::<Token![>]>()?;
+            Ok(Attribute::Type(tag, inner))
+        } else if input.peek2(Token![::]) {
+            let inner = input.parse()?;
+            Ok(Attribute::Attr(inner))
+        } else if input.peek2(token::Paren) {
+            let tag = input.parse()?;
+            let content;
+            let _ = syn::parenthesized!(content in input);
+            let args = Punctuated::<Ident, Token![,]>::parse_terminated(&content)?
+                .into_iter()
+                .collect();
+
+            Ok(Attribute::Tagged(tag, args))
+        } else {
+            Ok(Attribute::Attr(input.parse()?))
+        }
+    }
+}
+
+fn parse_vector_trailing_till_double_comma<T, P>(input: ParseStream) -> syn::Result<Vec<T>>
+where
+    T: Parse,
+    P: syn::token::Token + Parse,
+{
+    Ok(
+        Punctuated::<Option<T>, P>::parse_separated_nonempty_with(input, |input_tokens| {
+            if input_tokens.is_empty() || input_tokens.peek(Token![::]) {
+                Ok(None)
+            } else {
+                T::parse(input_tokens).map(Some)
+            }
+        })?
+        .into_iter()
+        .flatten()
+        .collect(),
+    )
+}
+
+#[allow(dead_code)]
+pub(crate) fn drain_stream(input: ParseStream) {
+    // JUST TO SKIP ALL
+    let _ = input.step(|cursor| {
+        let mut rest = *cursor;
+        while let Some((_, next)) = rest.token_tree() {
+            rest = next
+        }
+        Ok(((), rest))
+    });
+}
+
+#[derive(PartialEq, Debug, Clone, Default)]
+pub(crate) struct Positional(pub(crate) Vec<syn::Expr>);
+
+impl Parse for Positional {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self(
+            Punctuated::<syn::Expr, Token![,]>::parse_terminated(input)?
+                .into_iter()
+                .collect(),
+        ))
+    }
+}
+
+#[derive(PartialEq, Debug, Clone)]
+pub(crate) struct Fixture {
+    pub(crate) name: Ident,
+    pub(crate) resolve: Option<Ident>,
+    pub(crate) positional: Positional,
+}
+
+impl Fixture {
+    pub(crate) fn new(name: Ident, resolve: Option<Ident>, positional: Positional) -> Self {
+        Self {
+            name,
+            resolve,
+            positional,
+        }
+    }
+}
+
+impl Parse for Fixture {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let resolve = input.parse()?;
+        if input.peek(Paren) || input.peek(Token![as]) {
+            let positional = if input.peek(Paren) {
+                let content;
+                let _ = syn::parenthesized!(content in input);
+                content.parse()?
+            } else {
+                Default::default()
+            };
+
+            if input.peek(Token![as]) {
+                let _: Token![as] = input.parse()?;
+                Ok(Self::new(input.parse()?, Some(resolve), positional))
+            } else {
+                Ok(Self::new(resolve, None, positional))
+            }
+        } else {
+            Err(syn::Error::new(
+                input.span(),
+                "fixture need arguments or 'as new_name' format",
+            ))
+        }
+    }
+}
+
+impl RefIdent for Fixture {
+    fn ident(&self) -> &Ident {
+        &self.name
+    }
+}
+
+impl ToTokens for Fixture {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.name.to_tokens(tokens)
+    }
+}
+
+pub(crate) fn extract_fixtures(item_fn: &mut ItemFn) -> Result<Vec<Fixture>, ErrorsVec> {
+    let mut fixtures_extractor = FixturesFunctionExtractor::default();
+    fixtures_extractor.visit_item_fn_mut(item_fn);
+
+    if fixtures_extractor.1.is_empty() {
+        Ok(fixtures_extractor.0)
+    } else {
+        Err(fixtures_extractor.1.into())
+    }
+}
+
+pub(crate) fn extract_defaults(item_fn: &mut ItemFn) -> Result<Vec<ArgumentValue>, ErrorsVec> {
+    let mut defaults_extractor = DefaultsFunctionExtractor::default();
+    defaults_extractor.visit_item_fn_mut(item_fn);
+
+    if defaults_extractor.1.is_empty() {
+        Ok(defaults_extractor.0)
+    } else {
+        Err(defaults_extractor.1.into())
+    }
+}
+
+pub(crate) fn extract_default_return_type(
+    item_fn: &mut ItemFn,
+) -> Result<Option<syn::Type>, ErrorsVec> {
+    let mut default_type_extractor = DefaultTypeFunctionExtractor::default();
+    default_type_extractor.visit_item_fn_mut(item_fn);
+    default_type_extractor.take()
+}
+
+pub(crate) fn extract_partials_return_type(
+    item_fn: &mut ItemFn,
+) -> Result<Vec<(usize, syn::Type)>, ErrorsVec> {
+    let mut partials_type_extractor = PartialsTypeFunctionExtractor::default();
+    partials_type_extractor.visit_item_fn_mut(item_fn);
+    partials_type_extractor.take()
+}
+
+pub(crate) fn extract_once(item_fn: &mut ItemFn) -> Result<Option<Ident>, ErrorsVec> {
+    let mut extractor = IsOnceAttributeFunctionExtractor::default();
+    extractor.visit_item_fn_mut(item_fn);
+    extractor.take()
+}
+
+fn extract_argument_attrs<'a, B: 'a + std::fmt::Debug>(
+    node: &mut FnArg,
+    is_valid_attr: fn(&syn::Attribute) -> bool,
+    build: fn(syn::Attribute, &Ident) -> syn::Result<B>,
+) -> Box<dyn Iterator<Item = syn::Result<B>> + 'a> {
+    let name = node.maybe_ident().cloned();
+    if name.is_none() {
+        return Box::new(std::iter::empty());
+    }
+
+    let name = name.unwrap();
+    if let FnArg::Typed(ref mut arg) = node {
+        // Extract interesting attributes
+        let attrs = std::mem::take(&mut arg.attrs);
+        let (extracted, remain): (Vec<_>, Vec<_>) = attrs.into_iter().partition(is_valid_attr);
+
+        arg.attrs = remain;
+
+        // Parse attrs
+        Box::new(extracted.into_iter().map(move |attr| build(attr, &name)))
+    } else {
+        Box::new(std::iter::empty())
+    }
+}
+
+/// Simple struct used to visit function attributes and extract default return
+/// type
+struct DefaultTypeFunctionExtractor(Result<Option<syn::Type>, ErrorsVec>);
+
+impl DefaultTypeFunctionExtractor {
+    fn take(self) -> Result<Option<syn::Type>, ErrorsVec> {
+        self.0
+    }
+}
+
+impl Default for DefaultTypeFunctionExtractor {
+    fn default() -> Self {
+        Self(Ok(None))
+    }
+}
+
+impl VisitMut for DefaultTypeFunctionExtractor {
+    fn visit_item_fn_mut(&mut self, node: &mut ItemFn) {
+        let attrs = std::mem::take(&mut node.attrs);
+        let (defaults, remain): (Vec<_>, Vec<_>) = attrs
+            .into_iter()
+            .partition(|attr| attr_is(attr, FixtureModifiers::DEFAULT_RET_ATTR));
+
+        node.attrs = remain;
+        let mut defaults = defaults.into_iter();
+        let mut data = None;
+        let mut errors = ErrorsVec::default();
+        match defaults.next().map(|def| def.parse_args::<syn::Type>()) {
+            Some(Ok(t)) => data = Some(t),
+            Some(Err(e)) => errors.push(e),
+            None => {}
+        };
+        errors.extend(
+            defaults.map(|a| syn::Error::new_spanned(a, "You cannot use default more than once")),
+        );
+        self.0 = if errors.len() > 0 {
+            Err(errors)
+        } else {
+            Ok(data)
+        };
+
+        syn::visit_mut::visit_item_fn_mut(self, node);
+    }
+}
+
+/// Simple struct used to visit function attributes and extract default return
+/// type
+struct PartialsTypeFunctionExtractor(Result<Vec<(usize, syn::Type)>, ErrorsVec>);
+
+impl PartialsTypeFunctionExtractor {
+    fn take(self) -> Result<Vec<(usize, syn::Type)>, ErrorsVec> {
+        self.0
+    }
+}
+
+impl Default for PartialsTypeFunctionExtractor {
+    fn default() -> Self {
+        Self(Ok(Vec::default()))
+    }
+}
+
+impl VisitMut for PartialsTypeFunctionExtractor {
+    fn visit_item_fn_mut(&mut self, node: &mut ItemFn) {
+        let attrs = std::mem::take(&mut node.attrs);
+        let (partials, remain): (Vec<_>, Vec<_>) =
+            attrs
+                .into_iter()
+                .partition(|attr| match attr.path.get_ident() {
+                    Some(name) => name
+                        .to_string()
+                        .starts_with(FixtureModifiers::PARTIAL_RET_ATTR),
+                    None => false,
+                });
+
+        node.attrs = remain;
+        let mut errors = ErrorsVec::default();
+        let mut data: Vec<(usize, syn::Type)> = Vec::default();
+        for attr in partials {
+            match attr.parse_args::<syn::Type>() {
+                Ok(t) => {
+                    match attr.path.get_ident().unwrap().to_string()
+                        [FixtureModifiers::PARTIAL_RET_ATTR.len()..]
+                        .parse()
+                    {
+                        Ok(id) => data.push((id, t)),
+                        Err(_) => errors.push(syn::Error::new_spanned(
+                            attr,
+                            "Invalid partial syntax: should be partial_<n_arguments>",
+                        )),
+                    }
+                }
+                Err(e) => errors.push(e),
+            }
+        }
+        self.0 = if errors.len() > 0 {
+            Err(errors)
+        } else {
+            Ok(data)
+        };
+
+        syn::visit_mut::visit_item_fn_mut(self, node);
+    }
+}
+
+/// Simple struct used to visit function attributes and extract once
+/// type
+struct IsOnceAttributeFunctionExtractor(Result<Option<Ident>, ErrorsVec>);
+
+impl IsOnceAttributeFunctionExtractor {
+    fn take(self) -> Result<Option<Ident>, ErrorsVec> {
+        self.0
+    }
+}
+
+impl Default for IsOnceAttributeFunctionExtractor {
+    fn default() -> Self {
+        Self(Ok(None))
+    }
+}
+
+impl VisitMut for IsOnceAttributeFunctionExtractor {
+    fn visit_item_fn_mut(&mut self, node: &mut ItemFn) {
+        let attrs = std::mem::take(&mut node.attrs);
+        let (onces, remain): (Vec<_>, Vec<_>) =
+            attrs.into_iter().partition(|attr| attr_is(attr, "once"));
+
+        node.attrs = remain;
+        self.0 = match onces.len() {
+            1 => Ok(onces[0].path.get_ident().cloned()),
+            0 => Ok(None),
+            _ => Err(onces
+                .into_iter()
+                .skip(1)
+                .map(|attr| syn::Error::new_spanned(attr, "You cannot use #[once] more than once"))
+                .collect::<Vec<_>>()
+                .into()),
+        };
+        syn::visit_mut::visit_item_fn_mut(self, node);
+    }
+}
+
+/// Simple struct used to visit function attributes and extract case arguments and
+/// eventualy parsing errors
+#[derive(Default)]
+struct CaseArgsFunctionExtractor(Vec<Ident>, Vec<syn::Error>);
+
+impl VisitMut for CaseArgsFunctionExtractor {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        for r in extract_argument_attrs(node, |a| attr_is(a, "case"), |_a, name| Ok(name.clone())) {
+            match r {
+                Ok(value) => self.0.push(value),
+                Err(err) => self.1.push(err),
+            }
+        }
+
+        syn::visit_mut::visit_fn_arg_mut(self, node);
+    }
+}
+
+pub(crate) fn extract_case_args(item_fn: &mut ItemFn) -> Result<Vec<Ident>, ErrorsVec> {
+    let mut case_args_extractor = CaseArgsFunctionExtractor::default();
+    case_args_extractor.visit_item_fn_mut(item_fn);
+
+    if case_args_extractor.1.is_empty() {
+        Ok(case_args_extractor.0)
+    } else {
+        Err(case_args_extractor.1.into())
+    }
+}
+
+/// Simple struct used to visit function attributes and extract cases and
+/// eventualy parsing errors
+#[derive(Default)]
+struct CasesFunctionExtractor(Vec<TestCase>, Vec<syn::Error>);
+
+impl VisitMut for CasesFunctionExtractor {
+    fn visit_item_fn_mut(&mut self, node: &mut ItemFn) {
+        let attrs = std::mem::take(&mut node.attrs);
+        let mut attrs_buffer = Default::default();
+        let case: syn::PathSegment = parse_quote! { case };
+        for attr in attrs.into_iter() {
+            if attr_starts_with(&attr, &case) {
+                match attr.parse_args::<Expressions>() {
+                    Ok(expressions) => {
+                        let description = attr.path.segments.into_iter().nth(1).map(|p| p.ident);
+                        self.0.push(TestCase {
+                            args: expressions.into(),
+                            attrs: std::mem::take(&mut attrs_buffer),
+                            description,
+                        });
+                    }
+                    Err(err) => self.1.push(err),
+                };
+            } else {
+                attrs_buffer.push(attr)
+            }
+        }
+        node.attrs = std::mem::take(&mut attrs_buffer);
+        syn::visit_mut::visit_item_fn_mut(self, node);
+    }
+}
+
+pub(crate) fn extract_cases(item_fn: &mut ItemFn) -> Result<Vec<TestCase>, ErrorsVec> {
+    let mut cases_extractor = CasesFunctionExtractor::default();
+    cases_extractor.visit_item_fn_mut(item_fn);
+
+    if cases_extractor.1.is_empty() {
+        Ok(cases_extractor.0)
+    } else {
+        Err(cases_extractor.1.into())
+    }
+}
+
+/// Simple struct used to visit function attributes and extract value list and
+/// eventualy parsing errors
+#[derive(Default)]
+struct ValueListFunctionExtractor(Vec<ValueList>, Vec<syn::Error>);
+
+impl VisitMut for ValueListFunctionExtractor {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        for r in extract_argument_attrs(
+            node,
+            |a| attr_is(a, "values"),
+            |a, name| {
+                a.parse_args::<Expressions>().map(|v| ValueList {
+                    arg: name.clone(),
+                    values: v.take(),
+                })
+            },
+        ) {
+            match r {
+                Ok(vlist) => self.0.push(vlist),
+                Err(err) => self.1.push(err),
+            }
+        }
+
+        syn::visit_mut::visit_fn_arg_mut(self, node);
+    }
+}
+
+pub(crate) fn extract_value_list(item_fn: &mut ItemFn) -> Result<Vec<ValueList>, ErrorsVec> {
+    let mut vlist_extractor = ValueListFunctionExtractor::default();
+    vlist_extractor.visit_item_fn_mut(item_fn);
+
+    if vlist_extractor.1.is_empty() {
+        Ok(vlist_extractor.0)
+    } else {
+        Err(vlist_extractor.1.into())
+    }
+}
+
+/// Simple struct used to visit function args attributes to extract the
+/// excluded ones and eventualy parsing errors
+struct ExcludedTraceAttributesFunctionExtractor(Result<Vec<Ident>, ErrorsVec>);
+impl From<Result<Vec<Ident>, ErrorsVec>> for ExcludedTraceAttributesFunctionExtractor {
+    fn from(inner: Result<Vec<Ident>, ErrorsVec>) -> Self {
+        Self(inner)
+    }
+}
+
+impl ExcludedTraceAttributesFunctionExtractor {
+    pub(crate) fn take(self) -> Result<Vec<Ident>, ErrorsVec> {
+        self.0
+    }
+
+    fn update_error(&mut self, mut errors: ErrorsVec) {
+        match &mut self.0 {
+            Ok(_) => self.0 = Err(errors),
+            Err(err) => err.append(&mut errors),
+        }
+    }
+
+    fn update_excluded(&mut self, value: Ident) {
+        if let Some(inner) = self.0.iter_mut().next() {
+            inner.push(value);
+        }
+    }
+}
+
+impl Default for ExcludedTraceAttributesFunctionExtractor {
+    fn default() -> Self {
+        Self(Ok(Default::default()))
+    }
+}
+
+impl VisitMut for ExcludedTraceAttributesFunctionExtractor {
+    fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
+        for r in
+            extract_argument_attrs(node, |a| attr_is(a, "notrace"), |_a, name| Ok(name.clone()))
+        {
+            match r {
+                Ok(value) => self.update_excluded(value),
+                Err(err) => self.update_error(err.into()),
+            }
+        }
+
+        syn::visit_mut::visit_fn_arg_mut(self, node);
+    }
+}
+
+pub(crate) fn extract_excluded_trace(item_fn: &mut ItemFn) -> Result<Vec<Ident>, ErrorsVec> {
+    let mut excluded_trace_extractor = ExcludedTraceAttributesFunctionExtractor::default();
+    excluded_trace_extractor.visit_item_fn_mut(item_fn);
+    excluded_trace_extractor.take()
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::test::*;
+
+    mod parse_attributes {
+        use super::assert_eq;
+        use super::*;
+
+        fn parse_attributes<S: AsRef<str>>(attributes: S) -> Attributes {
+            parse_meta(attributes)
+        }
+
+        #[test]
+        fn one_simple_ident() {
+            let attributes = parse_attributes("my_ident");
+
+            let expected = Attributes {
+                attributes: vec![Attribute::attr("my_ident")],
+            };
+
+            assert_eq!(expected, attributes);
+        }
+
+        #[test]
+        fn one_simple_group() {
+            let attributes = parse_attributes("group_tag(first, second)");
+
+            let expected = Attributes {
+                attributes: vec![Attribute::tagged("group_tag", vec!["first", "second"])],
+            };
+
+            assert_eq!(expected, attributes);
+        }
+
+        #[test]
+        fn one_simple_type() {
+            let attributes = parse_attributes("type_tag<(u32, T, (String, i32))>");
+
+            let expected = Attributes {
+                attributes: vec![Attribute::typed("type_tag", "(u32, T, (String, i32))")],
+            };
+
+            assert_eq!(expected, attributes);
+        }
+
+        #[test]
+        fn integrated() {
+            let attributes = parse_attributes(
+                r#"
+            simple :: tagged(first, second) :: type_tag<(u32, T, (std::string::String, i32))> :: more_tagged(a,b)"#,
+            );
+
+            let expected = Attributes {
+                attributes: vec![
+                    Attribute::attr("simple"),
+                    Attribute::tagged("tagged", vec!["first", "second"]),
+                    Attribute::typed("type_tag", "(u32, T, (std::string::String, i32))"),
+                    Attribute::tagged("more_tagged", vec!["a", "b"]),
+                ],
+            };
+
+            assert_eq!(expected, attributes);
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/rstest.rs b/third_party/rust/rstest/v0_12/crate/src/parse/rstest.rs
new file mode 100644
index 0000000..7ad00ea
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/rstest.rs
@@ -0,0 +1,860 @@
+use syn::{
+    parse::{Parse, ParseStream},
+    Ident, ItemFn, Token,
+};
+
+use super::testcase::TestCase;
+use super::{
+    extract_case_args, extract_cases, extract_excluded_trace, extract_fixtures, extract_value_list,
+    parse_vector_trailing_till_double_comma, Attribute, Attributes, ExtendWithFunctionAttrs,
+    Fixture,
+};
+use crate::parse::vlist::ValueList;
+use crate::{
+    error::ErrorsVec,
+    refident::{MaybeIdent, RefIdent},
+};
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, ToTokens};
+
+#[derive(PartialEq, Debug, Default)]
+pub(crate) struct RsTestInfo {
+    pub(crate) data: RsTestData,
+    pub(crate) attributes: RsTestAttributes,
+}
+
+impl Parse for RsTestInfo {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(if input.is_empty() {
+            Default::default()
+        } else {
+            Self {
+                data: input.parse()?,
+                attributes: input
+                    .parse::<Token![::]>()
+                    .or_else(|_| Ok(Default::default()))
+                    .and_then(|_| input.parse())?,
+            }
+        })
+    }
+}
+
+impl ExtendWithFunctionAttrs for RsTestInfo {
+    fn extend_with_function_attrs(&mut self, item_fn: &mut ItemFn) -> Result<(), ErrorsVec> {
+        let (_, excluded) = merge_errors!(
+            self.data.extend_with_function_attrs(item_fn),
+            extract_excluded_trace(item_fn)
+        )?;
+        self.attributes.add_notraces(excluded);
+        Ok(())
+    }
+}
+
+#[derive(PartialEq, Debug, Default)]
+pub(crate) struct RsTestData {
+    pub(crate) items: Vec<RsTestItem>,
+}
+
+impl RsTestData {
+    pub(crate) fn case_args(&self) -> impl Iterator<Item = &Ident> {
+        self.items.iter().filter_map(|it| match it {
+            RsTestItem::CaseArgName(ref arg) => Some(arg),
+            _ => None,
+        })
+    }
+
+    #[allow(dead_code)]
+    pub(crate) fn has_case_args(&self) -> bool {
+        self.case_args().next().is_some()
+    }
+
+    pub(crate) fn cases(&self) -> impl Iterator<Item = &TestCase> {
+        self.items.iter().filter_map(|it| match it {
+            RsTestItem::TestCase(ref case) => Some(case),
+            _ => None,
+        })
+    }
+
+    pub(crate) fn has_cases(&self) -> bool {
+        self.cases().next().is_some()
+    }
+
+    pub(crate) fn fixtures(&self) -> impl Iterator<Item = &Fixture> {
+        self.items.iter().filter_map(|it| match it {
+            RsTestItem::Fixture(ref fixture) => Some(fixture),
+            _ => None,
+        })
+    }
+
+    #[allow(dead_code)]
+    pub(crate) fn has_fixtures(&self) -> bool {
+        self.fixtures().next().is_some()
+    }
+
+    pub(crate) fn list_values(&self) -> impl Iterator<Item = &ValueList> {
+        self.items.iter().filter_map(|mv| match mv {
+            RsTestItem::ValueList(ref value_list) => Some(value_list),
+            _ => None,
+        })
+    }
+
+    pub(crate) fn has_list_values(&self) -> bool {
+        self.list_values().next().is_some()
+    }
+}
+
+impl Parse for RsTestData {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek(Token![::]) {
+            Ok(Default::default())
+        } else {
+            Ok(Self {
+                items: parse_vector_trailing_till_double_comma::<_, Token![,]>(input)?,
+            })
+        }
+    }
+}
+
+impl ExtendWithFunctionAttrs for RsTestData {
+    fn extend_with_function_attrs(&mut self, item_fn: &mut ItemFn) -> Result<(), ErrorsVec> {
+        let composed_tuple!(fixtures, case_args, cases, value_list) = merge_errors!(
+            extract_fixtures(item_fn),
+            extract_case_args(item_fn),
+            extract_cases(item_fn),
+            extract_value_list(item_fn)
+        )?;
+
+        self.items.extend(fixtures.into_iter().map(|f| f.into()));
+        self.items.extend(case_args.into_iter().map(|f| f.into()));
+        self.items.extend(cases.into_iter().map(|f| f.into()));
+        self.items.extend(value_list.into_iter().map(|f| f.into()));
+        Ok(())
+    }
+}
+
+#[derive(PartialEq, Debug)]
+pub(crate) enum RsTestItem {
+    Fixture(Fixture),
+    CaseArgName(Ident),
+    TestCase(TestCase),
+    ValueList(ValueList),
+}
+
+impl From<Fixture> for RsTestItem {
+    fn from(f: Fixture) -> Self {
+        RsTestItem::Fixture(f)
+    }
+}
+
+impl From<Ident> for RsTestItem {
+    fn from(ident: Ident) -> Self {
+        RsTestItem::CaseArgName(ident)
+    }
+}
+
+impl From<TestCase> for RsTestItem {
+    fn from(case: TestCase) -> Self {
+        RsTestItem::TestCase(case)
+    }
+}
+
+impl From<ValueList> for RsTestItem {
+    fn from(value_list: ValueList) -> Self {
+        RsTestItem::ValueList(value_list)
+    }
+}
+
+impl Parse for RsTestItem {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.fork().parse::<TestCase>().is_ok() {
+            input.parse::<TestCase>().map(RsTestItem::TestCase)
+        } else if input.peek2(Token![=>]) {
+            input.parse::<ValueList>().map(RsTestItem::ValueList)
+        } else if input.fork().parse::<Fixture>().is_ok() {
+            input.parse::<Fixture>().map(RsTestItem::Fixture)
+        } else if input.fork().parse::<Ident>().is_ok() {
+            input.parse::<Ident>().map(RsTestItem::CaseArgName)
+        } else {
+            Err(syn::Error::new(Span::call_site(), "Cannot parse it"))
+        }
+    }
+}
+
+impl MaybeIdent for RsTestItem {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        use RsTestItem::*;
+        match self {
+            Fixture(ref fixture) => Some(fixture.ident()),
+            CaseArgName(ref case_arg) => Some(case_arg),
+            ValueList(ref value_list) => Some(value_list.ident()),
+            TestCase(_) => None,
+        }
+    }
+}
+
+impl ToTokens for RsTestItem {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        use RsTestItem::*;
+        match self {
+            Fixture(ref fixture) => fixture.to_tokens(tokens),
+            CaseArgName(ref case_arg) => case_arg.to_tokens(tokens),
+            TestCase(ref case) => case.to_tokens(tokens),
+            ValueList(ref list) => list.to_tokens(tokens),
+        }
+    }
+}
+
+wrap_attributes!(RsTestAttributes);
+
+impl RsTestAttributes {
+    const TRACE_VARIABLE_ATTR: &'static str = "trace";
+    const NOTRACE_VARIABLE_ATTR: &'static str = "notrace";
+
+    pub(crate) fn trace_me(&self, ident: &Ident) -> bool {
+        if self.should_trace() {
+            !self.iter().any(|m| Self::is_notrace(ident, m))
+        } else {
+            false
+        }
+    }
+
+    fn is_notrace(ident: &Ident, m: &Attribute) -> bool {
+        match m {
+            Attribute::Tagged(i, args) if i == Self::NOTRACE_VARIABLE_ATTR => {
+                args.iter().any(|a| a == ident)
+            }
+            _ => false,
+        }
+    }
+
+    pub(crate) fn should_trace(&self) -> bool {
+        self.iter().any(Self::is_trace)
+    }
+
+    pub(crate) fn add_trace(&mut self, trace: Ident) {
+        self.inner.attributes.push(Attribute::Attr(trace));
+    }
+
+    pub(crate) fn add_notraces(&mut self, notraces: Vec<Ident>) {
+        if notraces.is_empty() {
+            return;
+        }
+        self.inner.attributes.push(Attribute::Tagged(
+            format_ident!("{}", Self::NOTRACE_VARIABLE_ATTR),
+            notraces,
+        ));
+    }
+
+    fn is_trace(m: &Attribute) -> bool {
+        matches!(m, Attribute::Attr(i) if i == Self::TRACE_VARIABLE_ATTR)
+    }
+}
+
+impl Parse for RsTestAttributes {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(input.parse::<Attributes>()?.into())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test::*;
+
+    mod parse_rstest_data {
+        use super::assert_eq;
+        use super::*;
+
+        fn parse_rstest_data<S: AsRef<str>>(fixtures: S) -> RsTestData {
+            parse_meta(fixtures)
+        }
+
+        #[test]
+        fn one_arg() {
+            let fixtures = parse_rstest_data("my_fixture(42)");
+
+            let expected = RsTestData {
+                items: vec![fixture("my_fixture", &["42"]).into()],
+            };
+
+            assert_eq!(expected, fixtures);
+        }
+    }
+
+    fn parse_rstest<S: AsRef<str>>(rstest_data: S) -> RsTestInfo {
+        parse_meta(rstest_data)
+    }
+
+    mod no_cases {
+        use super::{assert_eq, *};
+        use crate::parse::{Attribute, Attributes};
+
+        #[test]
+        fn happy_path() {
+            let data = parse_rstest(
+                r#"my_fixture(42, "other"), other(vec![42])
+            :: trace :: no_trace(some)"#,
+            );
+
+            let expected = RsTestInfo {
+                data: vec![
+                    fixture("my_fixture", &["42", r#""other""#]).into(),
+                    fixture("other", &["vec![42]"]).into(),
+                ]
+                .into(),
+                attributes: Attributes {
+                    attributes: vec![
+                        Attribute::attr("trace"),
+                        Attribute::tagged("no_trace", vec!["some"]),
+                    ],
+                }
+                .into(),
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        mod fixture_extraction {
+            use super::{assert_eq, *};
+
+            #[test]
+            fn rename() {
+                let data = parse_rstest(
+                    r#"long_fixture_name(42, "other") as short, simple as s, no_change()"#,
+                );
+
+                let expected = RsTestInfo {
+                    data: vec![
+                        fixture("short", &["42", r#""other""#])
+                            .with_resolve("long_fixture_name")
+                            .into(),
+                        fixture("s", &[]).with_resolve("simple").into(),
+                        fixture("no_change", &[]).into(),
+                    ]
+                    .into(),
+                    ..Default::default()
+                };
+
+                assert_eq!(expected, data);
+            }
+
+            #[test]
+            fn rename_with_attributes() {
+                let mut item_fn = r#"
+                    fn test_fn(
+                        #[from(long_fixture_name)] 
+                        #[with(42, "other")] short: u32, 
+                        #[from(simple)]
+                        s: &str,
+                        no_change: i32) {
+                    }
+                    "#
+                .ast();
+
+                let expected = RsTestInfo {
+                    data: vec![
+                        fixture("short", &["42", r#""other""#])
+                            .with_resolve("long_fixture_name")
+                            .into(),
+                        fixture("s", &[]).with_resolve("simple").into(),
+                    ]
+                    .into(),
+                    ..Default::default()
+                };
+
+                let mut data = RsTestInfo::default();
+
+                data.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                assert_eq!(expected, data);
+            }
+
+            #[test]
+            fn defined_via_with_attributes() {
+                let mut item_fn = r#"
+                    fn test_fn(#[with(42, "other")] my_fixture: u32, #[with(vec![42])] other: &str) {
+                    }
+                    "#
+                .ast();
+
+                let expected = RsTestInfo {
+                    data: vec![
+                        fixture("my_fixture", &["42", r#""other""#]).into(),
+                        fixture("other", &["vec![42]"]).into(),
+                    ]
+                    .into(),
+                    ..Default::default()
+                };
+
+                let mut data = RsTestInfo::default();
+
+                data.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                assert_eq!(expected, data);
+            }
+        }
+
+        #[test]
+        fn empty_fixtures() {
+            let data = parse_rstest(r#"::trace::no_trace(some)"#);
+
+            let expected = RsTestInfo {
+                attributes: Attributes {
+                    attributes: vec![
+                        Attribute::attr("trace"),
+                        Attribute::tagged("no_trace", vec!["some"]),
+                    ],
+                }
+                .into(),
+                ..Default::default()
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        #[test]
+        fn empty_attributes() {
+            let data = parse_rstest(r#"my_fixture(42, "other")"#);
+
+            let expected = RsTestInfo {
+                data: vec![fixture("my_fixture", &["42", r#""other""#]).into()].into(),
+                ..Default::default()
+            };
+
+            assert_eq!(expected, data);
+        }
+
+        #[test]
+        fn extract_notrace_args_atttribute() {
+            let mut item_fn = r#"
+            fn test_fn(#[notrace] a: u32, #[something_else] b: &str, #[notrace] c: i32) {
+            }
+            "#
+            .ast();
+
+            let mut info = RsTestInfo::default();
+
+            info.extend_with_function_attrs(&mut item_fn).unwrap();
+            info.attributes.add_trace(ident("trace"));
+
+            assert!(!info.attributes.trace_me(&ident("a")));
+            assert!(info.attributes.trace_me(&ident("b")));
+            assert!(!info.attributes.trace_me(&ident("c")));
+            let b_args = item_fn
+                .sig
+                .inputs
+                .into_iter()
+                .nth(1)
+                .and_then(|id| match id {
+                    syn::FnArg::Typed(arg) => Some(arg.attrs),
+                    _ => None,
+                })
+                .unwrap();
+            assert_eq!(attrs("#[something_else]"), b_args);
+        }
+    }
+
+    mod parametrize_cases {
+        use super::{assert_eq, *};
+        use std::iter::FromIterator;
+
+        #[test]
+        fn one_simple_case_one_arg() {
+            let data = parse_rstest(r#"arg, case(42)"#).data;
+
+            let args = data.case_args().collect::<Vec<_>>();
+            let cases = data.cases().collect::<Vec<_>>();
+
+            assert_eq!(1, args.len());
+            assert_eq!(1, cases.len());
+            assert_eq!("arg", &args[0].to_string());
+            assert_eq!(to_args!(["42"]), cases[0].args())
+        }
+
+        #[test]
+        fn happy_path() {
+            let info = parse_rstest(
+                r#"
+                my_fixture(42,"foo"),
+                arg1, arg2, arg3,
+                case(1,2,3),
+                case(11,12,13),
+                case(21,22,23)
+            "#,
+            );
+
+            let data = info.data;
+            let fixtures = data.fixtures().cloned().collect::<Vec<_>>();
+
+            assert_eq!(vec![fixture("my_fixture", &["42", r#""foo""#])], fixtures);
+            assert_eq!(
+                to_strs!(vec!["arg1", "arg2", "arg3"]),
+                data.case_args()
+                    .map(ToString::to_string)
+                    .collect::<Vec<_>>()
+            );
+
+            let cases = data.cases().collect::<Vec<_>>();
+
+            assert_eq!(3, cases.len());
+            assert_eq!(to_args!(["1", "2", "3"]), cases[0].args());
+            assert_eq!(to_args!(["11", "12", "13"]), cases[1].args());
+            assert_eq!(to_args!(["21", "22", "23"]), cases[2].args());
+        }
+
+        mod defined_via_with_attributes {
+            use super::{assert_eq, *};
+
+            #[test]
+            fn one_case() {
+                let mut item_fn = r#"
+                #[case::first(42, "first")]
+                fn test_fn(#[case] arg1: u32, #[case] arg2: &str) {
+                }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                let case_args = info.data.case_args().cloned().collect::<Vec<_>>();
+                let cases = info.data.cases().cloned().collect::<Vec<_>>();
+
+                assert_eq!(to_idents!(["arg1", "arg2"]), case_args);
+                assert_eq!(
+                    vec![
+                        TestCase::from_iter(["42", r#""first""#].iter()).with_description("first"),
+                    ],
+                    cases
+                );
+            }
+
+            #[test]
+            fn parse_tuple_value() {
+                let mut item_fn = r#"
+                #[case(42, (24, "first"))]
+                fn test_fn(#[case] arg1: u32, #[case] tupled: (u32, &str)) {
+                }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                let cases = info.data.cases().cloned().collect::<Vec<_>>();
+
+                assert_eq!(
+                    vec![TestCase::from_iter(["42", r#"(24, "first")"#].iter()),],
+                    cases
+                );
+            }
+
+            #[test]
+            fn more_cases() {
+                let mut item_fn = r#"
+                #[case::first(42, "first")]
+                #[case(24, "second")]
+                #[case::third(0, "third")]
+                fn test_fn(#[case] arg1: u32, #[case] arg2: &str) {
+                }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                let case_args = info.data.case_args().cloned().collect::<Vec<_>>();
+                let cases = info.data.cases().cloned().collect::<Vec<_>>();
+
+                assert_eq!(to_idents!(["arg1", "arg2"]), case_args);
+                assert_eq!(
+                    vec![
+                        TestCase::from_iter(["42", r#""first""#].iter()).with_description("first"),
+                        TestCase::from_iter(["24", r#""second""#].iter()),
+                        TestCase::from_iter(["0", r#""third""#].iter()).with_description("third"),
+                    ],
+                    cases
+                );
+            }
+
+            #[test]
+            fn should_collect_attributes() {
+                let mut item_fn = r#"
+                    #[first]
+                    #[first2(42)]
+                    #[case(42)]
+                    #[second]
+                    #[case(24)]
+                    #[global]
+                    fn test_fn(#[case] arg: u32) {
+                    }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                let cases = info.data.cases().cloned().collect::<Vec<_>>();
+
+                assert_eq!(
+                    vec![
+                        TestCase::from_iter(["42"].iter()).with_attrs(attrs(
+                            "
+                                #[first]
+                                #[first2(42)]
+                            "
+                        )),
+                        TestCase::from_iter(["24"].iter()).with_attrs(attrs(
+                            "
+                            #[second]
+                        "
+                        )),
+                    ],
+                    cases
+                );
+            }
+
+            #[test]
+            fn should_consume_all_used_attributes() {
+                let mut item_fn = r#"
+                    #[first]
+                    #[first2(42)]
+                    #[case(42)]
+                    #[second]
+                    #[case(24)]
+                    #[global]
+                    fn test_fn(#[case] arg: u32) {
+                    }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                assert_eq!(
+                    item_fn.attrs,
+                    attrs(
+                        "
+                        #[global]
+                        "
+                    )
+                );
+                assert!(!format!("{:?}", item_fn).contains("case"));
+            }
+
+            #[test]
+            fn should_report_all_errors() {
+                let mut item_fn = r#"
+                    #[case(#case_error#)]
+                    fn test_fn(#[case] arg: u32, #[with(#fixture_error#)] err_fixture: u32) {
+                    }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                let errors = info.extend_with_function_attrs(&mut item_fn).unwrap_err();
+
+                assert_eq!(2, errors.len());
+            }
+        }
+
+        #[test]
+        fn should_accept_comma_at_the_end_of_cases() {
+            let data = parse_rstest(
+                r#"
+                arg,
+                case(42),
+            "#,
+            )
+            .data;
+
+            let args = data.case_args().collect::<Vec<_>>();
+            let cases = data.cases().collect::<Vec<_>>();
+
+            assert_eq!(1, args.len());
+            assert_eq!(1, cases.len());
+            assert_eq!("arg", &args[0].to_string());
+            assert_eq!(to_args!(["42"]), cases[0].args())
+        }
+
+        #[test]
+        #[should_panic]
+        fn should_not_accept_invalid_separator_from_args_and_cases() {
+            parse_rstest(
+                r#"
+                ret
+                case::should_success(Ok(())),
+                case::should_fail(Err("Return Error"))
+            "#,
+            );
+        }
+
+        #[test]
+        fn case_could_be_arg_name() {
+            let data = parse_rstest(
+                r#"
+                case,
+                case(42)
+            "#,
+            )
+            .data;
+
+            assert_eq!("case", &data.case_args().next().unwrap().to_string());
+
+            let cases = data.cases().collect::<Vec<_>>();
+
+            assert_eq!(1, cases.len());
+            assert_eq!(to_args!(["42"]), cases[0].args());
+        }
+    }
+
+    mod matrix_cases {
+        use crate::parse::Attribute;
+
+        use super::{assert_eq, *};
+
+        #[test]
+        fn happy_path() {
+            let info = parse_rstest(
+                r#"
+                    expected => [12, 34 * 2],
+                    input => [format!("aa_{}", 2), "other"],
+                "#,
+            );
+
+            let value_ranges = info.data.list_values().collect::<Vec<_>>();
+            assert_eq!(2, value_ranges.len());
+            assert_eq!(to_args!(["12", "34 * 2"]), value_ranges[0].args());
+            assert_eq!(
+                to_args!([r#"format!("aa_{}", 2)"#, r#""other""#]),
+                value_ranges[1].args()
+            );
+            assert_eq!(info.attributes, Default::default());
+        }
+
+        #[test]
+        fn should_parse_attributes_too() {
+            let info = parse_rstest(
+                r#"
+                                        a => [12, 24, 42]
+                                        ::trace
+                                    "#,
+            );
+
+            assert_eq!(
+                info.attributes,
+                Attributes {
+                    attributes: vec![Attribute::attr("trace")]
+                }
+                .into()
+            );
+        }
+
+        #[test]
+        fn should_parse_injected_fixtures_too() {
+            let info = parse_rstest(
+                r#"
+                a => [12, 24, 42],
+                fixture_1(42, "foo"),
+                fixture_2("bar")
+                "#,
+            );
+
+            let fixtures = info.data.fixtures().cloned().collect::<Vec<_>>();
+
+            assert_eq!(
+                vec![
+                    fixture("fixture_1", &["42", r#""foo""#]),
+                    fixture("fixture_2", &[r#""bar""#])
+                ],
+                fixtures
+            );
+        }
+
+        #[test]
+        #[should_panic(expected = "should not be empty")]
+        fn should_not_compile_if_empty_expression_slice() {
+            parse_rstest(
+                r#"
+                invalid => []
+                "#,
+            );
+        }
+
+        mod defined_via_with_attributes {
+            use super::{assert_eq, *};
+
+            #[test]
+            fn one_arg() {
+                let mut item_fn = r#"
+                fn test_fn(#[values(1, 2, 1+2)] arg1: u32, #[values(format!("a"), "b b".to_owned(), String::new())] arg2: String) {
+                }
+                "#
+                .ast();
+
+                let mut info = RsTestInfo::default();
+
+                info.extend_with_function_attrs(&mut item_fn).unwrap();
+
+                let list_values = info.data.list_values().cloned().collect::<Vec<_>>();
+
+                assert_eq!(2, list_values.len());
+                assert_eq!(to_args!(["1", "2", "1+2"]), list_values[0].args());
+                assert_eq!(
+                    to_args!([r#"format!("a")"#, r#""b b".to_owned()"#, "String::new()"]),
+                    list_values[1].args()
+                );
+            }
+        }
+    }
+
+    mod integrated {
+        use super::{assert_eq, *};
+
+        #[test]
+        fn should_parse_fixture_cases_and_matrix_in_any_order() {
+            let data = parse_rstest(
+                r#"
+                u,
+                m => [1, 2],
+                case(42, A{}, D{}),
+                a,
+                case(43, A{}, D{}),
+                the_fixture(42),
+                mm => ["f", "oo", "BAR"],
+                d
+            "#,
+            )
+            .data;
+
+            let fixtures = data.fixtures().cloned().collect::<Vec<_>>();
+            assert_eq!(vec![fixture("the_fixture", &["42"])], fixtures);
+
+            assert_eq!(
+                to_strs!(vec!["u", "a", "d"]),
+                data.case_args()
+                    .map(ToString::to_string)
+                    .collect::<Vec<_>>()
+            );
+
+            let cases = data.cases().collect::<Vec<_>>();
+            assert_eq!(2, cases.len());
+            assert_eq!(to_args!(["42", "A{}", "D{}"]), cases[0].args());
+            assert_eq!(to_args!(["43", "A{}", "D{}"]), cases[1].args());
+
+            let value_ranges = data.list_values().collect::<Vec<_>>();
+            assert_eq!(2, value_ranges.len());
+            assert_eq!(to_args!(["1", "2"]), value_ranges[0].args());
+            assert_eq!(
+                to_args!([r#""f""#, r#""oo""#, r#""BAR""#]),
+                value_ranges[1].args()
+            );
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/testcase.rs b/third_party/rust/rstest/v0_12/crate/src/parse/testcase.rs
new file mode 100644
index 0000000..20efd79
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/testcase.rs
@@ -0,0 +1,162 @@
+use syn::{
+    parse::{Error, Parse, ParseStream, Result},
+    punctuated::Punctuated,
+    Attribute, Expr, Ident, Token,
+};
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+
+#[derive(PartialEq, Debug, Clone)]
+/// A test case instance data. Contains a list of arguments. It is parsed by parametrize
+/// attributes.
+pub(crate) struct TestCase {
+    pub(crate) args: Vec<Expr>,
+    pub(crate) attrs: Vec<Attribute>,
+    pub(crate) description: Option<Ident>,
+}
+
+impl Parse for TestCase {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let attrs = Attribute::parse_outer(input)?;
+        let case: Ident = input.parse()?;
+        if case == "case" {
+            let mut description = None;
+            if input.peek(Token![::]) {
+                let _ = input.parse::<Token![::]>();
+                description = Some(input.parse()?);
+            }
+            let content;
+            let _ = syn::parenthesized!(content in input);
+            let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?
+                .into_iter()
+                .collect();
+            Ok(TestCase {
+                args,
+                attrs,
+                description,
+            })
+        } else {
+            Err(Error::new(case.span(), "expected a test case"))
+        }
+    }
+}
+
+impl ToTokens for TestCase {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.args.iter().for_each(|c| c.to_tokens(tokens))
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::test::{assert_eq, *};
+
+    fn parse_test_case<S: AsRef<str>>(test_case: S) -> TestCase {
+        parse_meta(test_case)
+    }
+
+    #[test]
+    fn two_literal_args() {
+        let test_case = parse_test_case(r#"case(42, "value")"#);
+        let args = test_case.args();
+
+        let expected = to_args!(["42", r#""value""#]);
+
+        assert_eq!(expected, args);
+    }
+
+    #[test]
+    fn some_literals() {
+        let args_expressions = literal_expressions_str();
+        let test_case = parse_test_case(&format!("case({})", args_expressions.join(", ")));
+        let args = test_case.args();
+
+        assert_eq!(to_args!(args_expressions), args);
+    }
+
+    #[test]
+    fn accept_arbitrary_rust_code() {
+        let test_case = parse_test_case(r#"case(vec![1,2,3])"#);
+        let args = test_case.args();
+
+        assert_eq!(to_args!(["vec![1, 2, 3]"]), args);
+    }
+
+    #[test]
+    #[should_panic]
+    fn raise_error_on_invalid_rust_code() {
+        parse_test_case(r#"case(some:<>(1,2,3))"#);
+    }
+
+    #[test]
+    fn get_description_if_any() {
+        let test_case = parse_test_case(r#"case::this_test_description(42)"#);
+        let args = test_case.args();
+
+        assert_eq!(
+            "this_test_description",
+            &test_case.description.unwrap().to_string()
+        );
+        assert_eq!(to_args!(["42"]), args);
+    }
+
+    #[test]
+    fn get_description_also_with_more_args() {
+        let test_case = parse_test_case(r#"case :: this_test_description (42, 24)"#);
+        let args = test_case.args();
+
+        assert_eq!(
+            "this_test_description",
+            &test_case.description.unwrap().to_string()
+        );
+        assert_eq!(to_args!(["42", "24"]), args);
+    }
+
+    #[test]
+    fn parse_arbitrary_rust_code_as_expression() {
+        let test_case = parse_test_case(
+            r##"
+            case(42, -42,
+            pippo("pluto"),
+            Vec::new(),
+            String::from(r#"prrr"#),
+            {
+                let mut sum=0;
+                for i in 1..3 {
+                    sum += i;
+                }
+                sum
+            },
+            vec![1,2,3]
+        )"##,
+        );
+
+        let args = test_case.args();
+
+        assert_eq!(
+            to_args!([
+                "42",
+                "-42",
+                r#"pippo("pluto")"#,
+                "Vec::new()",
+                r##"String::from(r#"prrr"#)"##,
+                r#"{let mut sum=0;for i in 1..3 {sum += i;}sum}"#,
+                "vec![1,2,3]"
+            ]),
+            args
+        );
+    }
+
+    #[test]
+    fn save_attributes() {
+        let test_case = parse_test_case(r#"#[should_panic]#[other_attr(x)]case(42)"#);
+
+        let content = format!("{:?}", test_case.attrs);
+
+        assert_eq!(2, test_case.attrs.len());
+        assert!(content.contains("should_panic"));
+        assert!(content.contains("other_attr"));
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/parse/vlist.rs b/third_party/rust/rstest/v0_12/crate/src/parse/vlist.rs
new file mode 100644
index 0000000..43a5aab
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/parse/vlist.rs
@@ -0,0 +1,105 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    Expr, Ident, Token,
+};
+
+use crate::refident::RefIdent;
+
+use super::expressions::Expressions;
+
+#[derive(Debug, PartialEq, Clone)]
+pub(crate) struct ValueList {
+    pub(crate) arg: Ident,
+    pub(crate) values: Vec<Expr>,
+}
+
+impl Parse for ValueList {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let arg = input.parse()?;
+        let _to: Token![=>] = input.parse()?;
+        let content;
+        let paren = syn::bracketed!(content in input);
+        let values: Expressions = content.parse()?;
+
+        let ret = Self {
+            arg,
+            values: values.take(),
+        };
+        if ret.values.is_empty() {
+            Err(syn::Error::new(
+                paren.span,
+                "Values list should not be empty",
+            ))
+        } else {
+            Ok(ret)
+        }
+    }
+}
+
+impl RefIdent for ValueList {
+    fn ident(&self) -> &Ident {
+        &self.arg
+    }
+}
+
+impl ToTokens for ValueList {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.arg.to_tokens(tokens)
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use crate::test::{assert_eq, *};
+
+    use super::*;
+
+    mod parse_values_list {
+        use super::assert_eq;
+        use super::*;
+
+        fn parse_values_list<S: AsRef<str>>(values_list: S) -> ValueList {
+            parse_meta(values_list)
+        }
+
+        #[test]
+        fn some_literals() {
+            let literals = literal_expressions_str();
+            let name = "argument";
+
+            let values_list = parse_values_list(format!(
+                r#"{} => [{}]"#,
+                name,
+                literals
+                    .iter()
+                    .map(ToString::to_string)
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ));
+
+            assert_eq!(name, &values_list.arg.to_string());
+            assert_eq!(values_list.args(), to_args!(literals));
+        }
+
+        #[test]
+        fn raw_code() {
+            let values_list = parse_values_list(r#"no_mater => [vec![1,2,3]]"#);
+
+            assert_eq!(values_list.args(), to_args!(["vec![1, 2, 3]"]));
+        }
+
+        #[test]
+        #[should_panic]
+        fn raw_code_with_parsing_error() {
+            parse_values_list(r#"other => [some:<>(1,2,3)]"#);
+        }
+
+        #[test]
+        #[should_panic(expected = r#"expected square brackets"#)]
+        fn forget_brackets() {
+            parse_values_list(r#"other => 42"#);
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/refident.rs b/third_party/rust/rstest/v0_12/crate/src/refident.rs
new file mode 100644
index 0000000..2041bf41
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/refident.rs
@@ -0,0 +1,86 @@
+/// Provide `RefIdent` and `MaybeIdent` traits that give a shortcut to extract identity reference
+/// (`syn::Ident` struct).
+use proc_macro2::Ident;
+use syn::{FnArg, Pat, PatType, Type};
+
+pub trait RefIdent {
+    /// Return the reference to ident if any
+    fn ident(&self) -> &Ident;
+}
+
+pub trait MaybeIdent {
+    /// Return the reference to ident if any
+    fn maybe_ident(&self) -> Option<&Ident>;
+}
+
+impl<I: RefIdent> MaybeIdent for I {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        Some(self.ident())
+    }
+}
+
+impl RefIdent for Ident {
+    fn ident(&self) -> &Ident {
+        self
+    }
+}
+
+impl<'a> RefIdent for &'a Ident {
+    fn ident(&self) -> &Ident {
+        *self
+    }
+}
+
+impl MaybeIdent for FnArg {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        match self {
+            FnArg::Typed(PatType { pat, .. }) => match pat.as_ref() {
+                Pat::Ident(ident) => Some(&ident.ident),
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+}
+
+impl MaybeIdent for Type {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        match self {
+            Type::Path(tp) if tp.qself.is_none() => tp.path.get_ident(),
+            _ => None,
+        }
+    }
+}
+
+pub trait MaybeType {
+    /// Return the reference to type if any
+    fn maybe_type(&self) -> Option<&Type>;
+}
+
+impl MaybeType for FnArg {
+    fn maybe_type(&self) -> Option<&Type> {
+        match self {
+            FnArg::Typed(PatType { ty, .. }) => Some(ty.as_ref()),
+            _ => None,
+        }
+    }
+}
+
+impl MaybeIdent for syn::GenericParam {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        match self {
+            syn::GenericParam::Type(syn::TypeParam { ident, .. })
+            | syn::GenericParam::Const(syn::ConstParam { ident, .. }) => Some(ident),
+            syn::GenericParam::Lifetime(syn::LifetimeDef { lifetime, .. }) => Some(&lifetime.ident),
+        }
+    }
+}
+
+impl MaybeIdent for crate::parse::Attribute {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        use crate::parse::Attribute::*;
+        match self {
+            Attr(ident) | Tagged(ident, _) | Type(ident, _) => Some(ident),
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/render/fixture.rs b/third_party/rust/rstest/v0_12/crate/src/render/fixture.rs
new file mode 100644
index 0000000..d3699781
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/render/fixture.rs
@@ -0,0 +1,486 @@
+use proc_macro2::{Span, TokenStream};
+use syn::{parse_quote, Ident, ItemFn, ReturnType};
+
+use quote::quote;
+
+use super::{inject, render_exec_call};
+use crate::resolver::{self, Resolver};
+use crate::utils::{fn_args, fn_args_idents};
+use crate::{parse::fixture::FixtureInfo, utils::generics_clean_up};
+
+fn wrap_return_type_as_static_ref(rt: ReturnType) -> ReturnType {
+    match rt {
+        syn::ReturnType::Type(_, t) => parse_quote! {
+           -> &'static #t
+        },
+        o => o,
+    }
+}
+
+fn wrap_call_impl_with_call_once_impl(call_impl: TokenStream, rt: &ReturnType) -> TokenStream {
+    match rt {
+        syn::ReturnType::Type(_, t) => parse_quote! {
+            static mut S: Option<#t> = None;
+            static CELL: std::sync::Once = std::sync::Once::new();
+            CELL.call_once(|| unsafe { S = Some(#call_impl) });
+            unsafe { S.as_ref().unwrap() }
+        },
+        _ => parse_quote! {
+            static CELL: std::sync::Once = std::sync::Once::new();
+            CELL.call_once(|| #call_impl );
+        },
+    }
+}
+
+pub(crate) fn render(fixture: ItemFn, info: FixtureInfo) -> TokenStream {
+    let name = &fixture.sig.ident;
+    let asyncness = &fixture.sig.asyncness.clone();
+    let vargs = fn_args_idents(&fixture).cloned().collect::<Vec<_>>();
+    let args = &vargs;
+    let orig_args = &fixture.sig.inputs;
+    let orig_attrs = &fixture.attrs;
+    let generics = &fixture.sig.generics;
+    let mut default_output = info
+        .attributes
+        .extract_default_type()
+        .unwrap_or_else(|| fixture.sig.output.clone());
+    let default_generics =
+        generics_clean_up(&fixture.sig.generics, std::iter::empty(), &default_output);
+    let default_where_clause = &default_generics.where_clause;
+    let where_clause = &fixture.sig.generics.where_clause;
+    let mut output = fixture.sig.output.clone();
+    let visibility = &fixture.vis;
+    let resolver = (
+        resolver::fixtures::get(info.data.fixtures()),
+        resolver::values::get(info.data.values()),
+    );
+    let generics_idents = generics
+        .type_params()
+        .map(|tp| &tp.ident)
+        .cloned()
+        .collect::<Vec<_>>();
+    let inject = inject::resolve_aruments(fixture.sig.inputs.iter(), &resolver, &generics_idents);
+    let partials =
+        (1..=orig_args.len()).map(|n| render_partial_impl(&fixture, n, &resolver, &info));
+
+    let call_get = render_exec_call(parse_quote! { Self::get }, args, asyncness.is_some());
+    let mut call_impl = render_exec_call(parse_quote! { #name }, args, asyncness.is_some());
+
+    if info.attributes.is_once() {
+        call_impl = wrap_call_impl_with_call_once_impl(call_impl, &output);
+        output = wrap_return_type_as_static_ref(output);
+        default_output = wrap_return_type_as_static_ref(default_output);
+    }
+
+    quote! {
+        #[allow(non_camel_case_types)]
+        #visibility struct #name {}
+
+        impl #name {
+            #(#orig_attrs)*
+            #[allow(unused_mut)]
+            pub #asyncness fn get #generics (#orig_args) #output #where_clause {
+                #call_impl
+            }
+
+            pub #asyncness fn default #default_generics () #default_output #default_where_clause {
+                #inject
+                #call_get
+            }
+
+            #(#partials)*
+        }
+
+        #[allow(dead_code)]
+        #fixture
+    }
+}
+
+fn render_partial_impl(
+    fixture: &ItemFn,
+    n: usize,
+    resolver: &impl Resolver,
+    info: &FixtureInfo,
+) -> TokenStream {
+    let mut output = info
+        .attributes
+        .extract_partial_type(n)
+        .unwrap_or_else(|| fixture.sig.output.clone());
+
+    if info.attributes.is_once() {
+        output = wrap_return_type_as_static_ref(output);
+    }
+
+    let generics = generics_clean_up(&fixture.sig.generics, fn_args(fixture).take(n), &output);
+    let where_clause = &generics.where_clause;
+    let asyncness = &fixture.sig.asyncness;
+
+    let genercs_idents = generics
+        .type_params()
+        .map(|tp| &tp.ident)
+        .cloned()
+        .collect::<Vec<_>>();
+    let inject =
+        inject::resolve_aruments(fixture.sig.inputs.iter().skip(n), resolver, &genercs_idents);
+
+    let sign_args = fn_args(fixture).take(n);
+    let fixture_args = fn_args_idents(fixture).cloned().collect::<Vec<_>>();
+    let name = Ident::new(&format!("partial_{}", n), Span::call_site());
+
+    let call_get = render_exec_call(
+        parse_quote! { Self::get },
+        &fixture_args,
+        asyncness.is_some(),
+    );
+
+    quote! {
+        #[allow(unused_mut)]
+        pub #asyncness fn #name #generics (#(#sign_args),*) #output #where_clause {
+            #inject
+            #call_get
+        }
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use syn::{
+        parse::{Parse, ParseStream},
+        parse2, parse_str, ItemFn, ItemImpl, ItemStruct, Result,
+    };
+
+    use crate::parse::{Attribute, Attributes};
+
+    use super::*;
+    use crate::test::{assert_eq, *};
+    use rstest_reuse::*;
+
+    #[derive(Clone)]
+    struct FixtureOutput {
+        orig: ItemFn,
+        fixture: ItemStruct,
+        core_impl: ItemImpl,
+    }
+
+    impl Parse for FixtureOutput {
+        fn parse(input: ParseStream) -> Result<Self> {
+            Ok(FixtureOutput {
+                fixture: input.parse()?,
+                core_impl: input.parse()?,
+                orig: input.parse()?,
+            })
+        }
+    }
+
+    fn parse_fixture<S: AsRef<str>>(code: S) -> (ItemFn, FixtureOutput) {
+        let item_fn = parse_str::<ItemFn>(code.as_ref()).unwrap();
+
+        let tokens = render(item_fn.clone(), Default::default());
+        (item_fn, parse2(tokens).unwrap())
+    }
+
+    fn test_maintains_function_visibility(code: &str) {
+        let (item_fn, out) = parse_fixture(code);
+
+        assert_eq!(item_fn.vis, out.fixture.vis);
+        assert_eq!(item_fn.vis, out.orig.vis);
+    }
+
+    fn select_method<S: AsRef<str>>(impl_code: ItemImpl, name: S) -> Option<syn::ImplItemMethod> {
+        impl_code
+            .items
+            .into_iter()
+            .filter_map(|ii| match ii {
+                syn::ImplItem::Method(f) => Some(f),
+                _ => None,
+            })
+            .find(|f| f.sig.ident == name.as_ref())
+    }
+
+    #[test]
+    fn maintains_pub_visibility() {
+        test_maintains_function_visibility(r#"pub fn test() { }"#);
+    }
+
+    #[test]
+    fn maintains_no_pub_visibility() {
+        test_maintains_function_visibility(r#"fn test() { }"#);
+    }
+
+    #[test]
+    fn implement_a_get_method_with_input_fixture_signature() {
+        let (item_fn, out) = parse_fixture(
+            r#"
+                    pub fn test<R: AsRef<str>, B>(mut s: String, v: &u32, a: &mut [i32], r: R) -> (u32, B, String, &str)
+                            where B: Borrow<u32>
+                    { }
+                    "#,
+        );
+
+        let mut signature = select_method(out.core_impl, "get").unwrap().sig;
+
+        signature.ident = item_fn.sig.ident.clone();
+
+        assert_eq!(item_fn.sig, signature);
+    }
+
+    #[test]
+    fn return_a_static_reference_if_once_attribute() {
+        let item_fn = parse_str::<ItemFn>(r#"
+                pub fn test<R: AsRef<str>, B>(mut s: String, v: &u32, a: &mut [i32], r: R) -> (u32, B, String, &str)
+                            where B: Borrow<u32>
+                    { }    
+        "#).unwrap();
+        let info = FixtureInfo::default().with_once();
+
+        let out: FixtureOutput = parse2(render(item_fn.clone(), info)).unwrap();
+
+        let signature = select_method(out.core_impl, "get").unwrap().sig;
+
+        assert_eq!(signature.output, "-> &'static (u32, B, String, &str)".ast())
+    }
+
+    #[template]
+    #[rstest(
+        method => ["default", "get", "partial_1", "partial_2", "partial_3"])
+    ]
+    #[case::async_fn(true)]
+    #[case::not_async_fn(false)]
+    fn async_fixture_cases(#[case] is_async: bool, method: &str) {}
+
+    #[apply(async_fixture_cases)]
+    fn fixture_method_should_be_async_if_fixture_function_is_async(
+        #[case] is_async: bool,
+        method: &str,
+    ) {
+        let prefix = if is_async { "async" } else { "" };
+        let (_, out) = parse_fixture(&format!(
+            r#"
+                    pub {} fn test(mut s: String, v: &u32, a: &mut [i32]) -> u32
+                            where B: Borrow<u32>
+                    {{ }}
+                    "#,
+            prefix
+        ));
+
+        let signature = select_method(out.core_impl, method).unwrap().sig;
+
+        assert_eq!(is_async, signature.asyncness.is_some());
+    }
+
+    #[apply(async_fixture_cases)]
+    fn fixture_method_should_use_await_if_fixture_function_is_async(
+        #[case] is_async: bool,
+        method: &str,
+    ) {
+        let prefix = if is_async { "async" } else { "" };
+        let (_, out) = parse_fixture(&format!(
+            r#"
+                    pub {} fn test(mut s: String, v: &u32, a: &mut [i32]) -> u32
+                    {{ }}
+                    "#,
+            prefix
+        ));
+
+        let body = select_method(out.core_impl, method).unwrap().block;
+        let last_statment = body.stmts.last().unwrap();
+        let is_await = match last_statment {
+            syn::Stmt::Expr(syn::Expr::Await(_)) => true,
+            _ => false,
+        };
+
+        assert_eq!(is_async, is_await);
+    }
+
+    #[test]
+    fn implement_a_default_method_with_input_cleaned_fixture_signature_and_no_args() {
+        let (item_fn, out) = parse_fixture(
+            r#"
+                    pub fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>(mut s: String, v: &u32, a: &mut [i32], r: R) -> (H, B, String, &str)
+                        where F: ToString,
+                        B: Borrow<u32>
+
+                    { }
+                    "#,
+        );
+
+        let default_decl = select_method(out.core_impl, "default").unwrap().sig;
+
+        let expected = parse_str::<ItemFn>(
+            r#"
+                    pub fn default<B, H: Iterator<Item=u32>>() -> (H, B, String, &str)
+                            where B: Borrow<u32>
+                    { }
+                    "#,
+        )
+        .unwrap();
+
+        assert_eq!(expected.sig.generics, default_decl.generics);
+        assert_eq!(item_fn.sig.output, default_decl.output);
+        assert!(default_decl.inputs.is_empty());
+    }
+
+    #[test]
+    fn use_default_return_type_if_any() {
+        let item_fn = parse_str::<ItemFn>(
+            r#"
+                    pub fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>() -> (H, B)
+                            where F: ToString,
+                            B: Borrow<u32>
+                    { }
+                    "#,
+        )
+        .unwrap();
+
+        let tokens = render(
+            item_fn.clone(),
+            FixtureInfo {
+                attributes: Attributes {
+                    attributes: vec![Attribute::Type(
+                        parse_str("default").unwrap(),
+                        parse_str("(impl Iterator<Item=u32>, B)").unwrap(),
+                    )],
+                }
+                .into(),
+                ..Default::default()
+            },
+        );
+        let out: FixtureOutput = parse2(tokens).unwrap();
+
+        let expected = parse_str::<syn::ItemFn>(
+            r#"
+                    pub fn default<B>() -> (impl Iterator<Item=u32>, B)
+                            where B: Borrow<u32>
+                    { }
+                    "#,
+        )
+        .unwrap();
+
+        let default_decl = select_method(out.core_impl, "default").unwrap().sig;
+
+        assert_eq!(expected.sig, default_decl);
+    }
+
+    #[test]
+    fn implement_partial_methods() {
+        let (item_fn, out) = parse_fixture(
+            r#"
+                    pub fn test(mut s: String, v: &u32, a: &mut [i32]) -> usize
+                    { }
+                    "#,
+        );
+
+        let partials = (1..=3)
+            .map(|n| {
+                select_method(out.core_impl.clone(), format!("partial_{}", n))
+                    .unwrap()
+                    .sig
+            })
+            .collect::<Vec<_>>();
+
+        // All 3 methods found
+
+        assert!(select_method(out.core_impl, "partial_4").is_none());
+
+        let expected_1 = parse_str::<ItemFn>(
+            r#"
+                    pub fn partial_1(mut s: String) -> usize
+                    { }
+                    "#,
+        )
+        .unwrap();
+
+        assert_eq!(expected_1.sig, partials[0]);
+        for p in partials {
+            assert_eq!(item_fn.sig.output, p.output);
+        }
+    }
+
+    #[rstest]
+    #[case::base("fn test<S: AsRef<str>, U: AsRef<u32>, F: ToString>(mut s: S, v: U) -> F {}",
+        vec![
+            "fn default<F: ToString>() -> F {}",
+            "fn partial_1<S: AsRef<str>, F: ToString>(mut s: S) -> F {}",
+            "fn partial_2<S: AsRef<str>, U: AsRef<u32>, F: ToString>(mut s: S, v: U) -> F {}",
+        ]
+    )]
+    #[case::associated_type("fn test<T: IntoIterator>(mut i: T) where T::Item: Copy {}",
+        vec![
+            "fn default() {}",
+            "fn partial_1<T: IntoIterator>(mut i: T) where T::Item: Copy {}",
+        ]
+    )]
+    #[case::not_remove_const_generics("fn test<const N:usize>(v: [u32; N]) -> [i32; N] {}",
+        vec![
+            "fn default<const N:usize>() -> [i32; N] {}",
+            "fn partial_1<const N:usize>(v: [u32; N]) -> [i32; N] {}",
+        ]
+    )]
+    #[case::remove_const_generics("fn test<const N:usize>(a: i32, v: [u32; N]) {}",
+        vec![
+            "fn default() {}",
+            "fn partial_1(a:i32) {}",
+            "fn partial_2<const N:usize>(a:i32, v: [u32; N]) {}",
+        ]
+    )]
+
+    fn clean_generics(#[case] code: &str, #[case] expected: Vec<&str>) {
+        let (item_fn, out) = parse_fixture(code);
+        let n_args = item_fn.sig.inputs.iter().count();
+
+        let mut signatures = vec![select_method(out.core_impl.clone(), "default").unwrap().sig];
+        signatures.extend((1..=n_args).map(|n| {
+            select_method(out.core_impl.clone(), format!("partial_{}", n))
+                .unwrap()
+                .sig
+        }));
+
+        let expected = expected
+            .into_iter()
+            .map(parse_str::<ItemFn>)
+            .map(|f| f.unwrap().sig)
+            .collect::<Vec<_>>();
+
+        assert_eq!(expected, signatures);
+    }
+
+    #[test]
+    fn use_partial_return_type_if_any() {
+        let item_fn = parse_str::<ItemFn>(
+            r#"
+                    pub fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>(h: H, b: B) -> (H, B)
+                            where F: ToString,
+                            B: Borrow<u32>
+                    { }
+                     "#,
+        )
+        .unwrap();
+
+        let tokens = render(
+            item_fn.clone(),
+            FixtureInfo {
+                attributes: Attributes {
+                    attributes: vec![Attribute::Type(
+                        parse_str("partial_1").unwrap(),
+                        parse_str("(H, impl Iterator<Item=u32>)").unwrap(),
+                    )],
+                }
+                .into(),
+                ..Default::default()
+            },
+        );
+        let out: FixtureOutput = parse2(tokens).unwrap();
+
+        let expected = parse_str::<syn::ItemFn>(
+            r#"
+                    pub fn partial_1<H: Iterator<Item=u32>>(h: H) -> (H, impl Iterator<Item=u32>)
+                    { }
+                    "#,
+        )
+        .unwrap();
+
+        let partial = select_method(out.core_impl, "partial_1").unwrap();
+
+        assert_eq!(expected.sig, partial.sig);
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/render/inject.rs b/third_party/rust/rstest/v0_12/crate/src/render/inject.rs
new file mode 100644
index 0000000..f17e4ee
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/render/inject.rs
@@ -0,0 +1,247 @@
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{parse_quote, Expr, FnArg, Ident, Stmt, Type};
+
+use crate::{
+    refident::{MaybeIdent, MaybeType},
+    resolver::Resolver,
+    utils::{fn_arg_mutability, IsLiteralExpression},
+};
+
+pub(crate) fn resolve_aruments<'a>(
+    args: impl Iterator<Item = &'a FnArg>,
+    resolver: &impl Resolver,
+    generic_types: &[Ident],
+) -> TokenStream {
+    let define_vars = args.map(|arg| ArgumentResolver::new(resolver, generic_types).resolve(arg));
+    quote! {
+        #(#define_vars)*
+    }
+}
+
+struct ArgumentResolver<'resolver, 'idents, 'f, R>
+where
+    R: Resolver + 'resolver,
+{
+    resolver: &'resolver R,
+    generic_types_names: &'idents [Ident],
+    magic_conversion: &'f dyn Fn(Cow<Expr>, &Type) -> Expr,
+}
+
+impl<'resolver, 'idents, 'f, R> ArgumentResolver<'resolver, 'idents, 'f, R>
+where
+    R: Resolver + 'resolver,
+{
+    fn new(resolver: &'resolver R, generic_types_names: &'idents [Ident]) -> Self {
+        Self {
+            resolver,
+            generic_types_names,
+            magic_conversion: &handling_magic_conversion_code,
+        }
+    }
+
+    fn resolve(&self, arg: &FnArg) -> Option<Stmt> {
+        let ident = arg.maybe_ident()?;
+        let mutability = fn_arg_mutability(arg);
+        let unused_mut: Option<syn::Attribute> = mutability
+            .as_ref()
+            .map(|_| parse_quote! {#[allow(unused_mut)]});
+        let arg_type = arg.maybe_type()?;
+        let fixture_name = self.fixture_name(ident);
+
+        let mut fixture = self
+            .resolver
+            .resolve(ident)
+            .or_else(|| self.resolver.resolve(&fixture_name))
+            .unwrap_or_else(|| default_fixture_resolve(&fixture_name));
+
+        if fixture.is_literal() && self.type_can_be_get_from_literal_str(arg_type) {
+            fixture = Cow::Owned((self.magic_conversion)(fixture, arg_type));
+        }
+        Some(parse_quote! {
+            #unused_mut
+            let #mutability #ident = #fixture;
+        })
+    }
+
+    fn fixture_name<'a>(&self, ident: &'a Ident) -> Cow<'a, Ident> {
+        let id_str = ident.to_string();
+        if id_str.starts_with('_') && !id_str.starts_with("__") {
+            Cow::Owned(Ident::new(&id_str[1..], ident.span()))
+        } else {
+            Cow::Borrowed(ident)
+        }
+    }
+
+    fn type_can_be_get_from_literal_str(&self, t: &Type) -> bool {
+        // Check valid type to apply magic conversion
+        match t {
+            Type::ImplTrait(_)
+            | Type::TraitObject(_)
+            | Type::Infer(_)
+            | Type::Group(_)
+            | Type::Macro(_)
+            | Type::Never(_)
+            | Type::Paren(_)
+            | Type::Verbatim(_)
+            | Type::Slice(_) => return false,
+            _ => {}
+        }
+        match t.maybe_ident() {
+            Some(id) => !self.generic_types_names.contains(id),
+            None => true,
+        }
+    }
+}
+
+fn default_fixture_resolve(ident: &Ident) -> Cow<Expr> {
+    Cow::Owned(parse_quote! { #ident::default() })
+}
+
+fn handling_magic_conversion_code(fixture: Cow<Expr>, arg_type: &Type) -> Expr {
+    parse_quote! {
+        {
+            struct __Wrap<T>(std::marker::PhantomData<T>);
+
+            trait __ViaParseDebug<'a, T> {
+                fn magic_conversion(&self, input: &'a str) -> T;
+            }
+
+            impl<'a, T> __ViaParseDebug<'a, T> for &&__Wrap<T>
+            where
+                T: std::str::FromStr,
+                T::Err: std::fmt::Debug,
+            {
+                fn magic_conversion(&self, input: &'a str) -> T {
+                    T::from_str(input).unwrap()
+                }
+            }
+
+            trait __ViaParse<'a, T> {
+                fn magic_conversion(&self, input: &'a str) -> T;
+            }
+
+            impl<'a, T> __ViaParse<'a, T> for &__Wrap<T>
+            where
+                T: std::str::FromStr,
+            {
+                fn magic_conversion(&self, input: &'a str) -> T {
+                    match T::from_str(input) {
+                        Ok(v) => v,
+                        Err(_) => {
+                            panic!("Cannot parse '{}' to get {}", input, std::stringify!(#arg_type));
+                        }
+                    }
+                }
+            }
+
+            trait __ViaIdent<'a, T> {
+                fn magic_conversion(&self, input: &'a str) -> T;
+            }
+
+            impl<'a> __ViaIdent<'a, &'a str> for &&__Wrap<&'a str> {
+                fn magic_conversion(&self, input: &'a str) -> &'a str {
+                    input
+                }
+            }
+            (&&&__Wrap::<#arg_type>(std::marker::PhantomData)).magic_conversion(#fixture)
+        }
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::{
+        test::{assert_eq, *},
+        utils::fn_args,
+    };
+
+    #[rstest]
+    #[case::as_is("fix: String", "let fix = fix::default();")]
+    #[case::without_underscore("_fix: String", "let _fix = fix::default();")]
+    #[case::do_not_remove_inner_underscores("f_i_x: String", "let f_i_x = f_i_x::default();")]
+    #[case::do_not_remove_double_underscore("__fix: String", "let __fix = __fix::default();")]
+    #[case::preserve_mut_but_annotate_as_allow_unused_mut(
+        "mut fix: String",
+        "#[allow(unused_mut)] let mut fix = fix::default();"
+    )]
+    fn call_fixture(#[case] arg_str: &str, #[case] expected: &str) {
+        let arg = arg_str.ast();
+
+        let injected = ArgumentResolver::new(&EmptyResolver {}, &[])
+            .resolve(&arg)
+            .unwrap();
+
+        assert_eq!(injected, expected.ast());
+    }
+
+    #[rstest]
+    #[case::as_is("fix: String", ("fix", expr("bar()")), "let fix = bar();")]
+    #[case::with_allow_unused_mut("mut fix: String", ("fix", expr("bar()")), "#[allow(unused_mut)] let mut fix = bar();")]
+    #[case::without_undescore("_fix: String", ("fix", expr("bar()")), "let _fix = bar();")]
+    #[case::without_remove_underscore_if_value("_orig: S", ("_orig", expr("S{}")), r#"let _orig = S{};"#)]
+    fn call_given_fixture(
+        #[case] arg_str: &str,
+        #[case] rule: (&str, Expr),
+        #[case] expected: &str,
+    ) {
+        let arg = arg_str.ast();
+        let mut resolver = std::collections::HashMap::new();
+        resolver.insert(rule.0.to_owned(), &rule.1);
+
+        let injected = ArgumentResolver::new(&resolver, &[]).resolve(&arg).unwrap();
+
+        assert_eq!(injected, expected.ast());
+    }
+
+    fn _mock_conversion_code(fixture: Cow<Expr>, arg_type: &Type) -> Expr {
+        parse_quote! {
+            #fixture as #arg_type
+        }
+    }
+
+    #[rstest]
+    #[case::implement_it(
+        "fn test(arg: MyType){}",
+        0,
+        r#"let arg = "value to convert" as MyType;"#
+    )]
+    #[case::discard_impl(
+        "fn test(arg: impl AsRef<str>){}",
+        0,
+        r#"let arg = "value to convert";"#
+    )]
+    #[case::discard_generic_type(
+        "fn test<S: AsRef<str>>(arg: S){}",
+        0,
+        r#"let arg = "value to convert";"#
+    )]
+    fn handle_magic_conversion(#[case] fn_str: &str, #[case] n_arg: usize, #[case] expected: &str) {
+        let function = fn_str.ast();
+        let arg = fn_args(&function).nth(n_arg).unwrap();
+        let generics = function
+            .sig
+            .generics
+            .type_params()
+            .map(|tp| &tp.ident)
+            .cloned()
+            .collect::<Vec<_>>();
+
+        let mut resolver = std::collections::HashMap::new();
+        let expr = expr(r#""value to convert""#);
+        resolver.insert(arg.maybe_ident().unwrap().to_string(), &expr);
+
+        let ag = ArgumentResolver {
+            resolver: &resolver,
+            generic_types_names: &generics,
+            magic_conversion: &_mock_conversion_code,
+        };
+
+        let injected = ag.resolve(&arg).unwrap();
+
+        assert_eq!(injected, expected.ast());
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/render/mod.rs b/third_party/rust/rstest/v0_12/crate/src/render/mod.rs
new file mode 100644
index 0000000..1b99879
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/render/mod.rs
@@ -0,0 +1,373 @@
+pub(crate) mod fixture;
+mod test;
+mod wrapper;
+
+use std::collections::HashMap;
+use syn::token::Async;
+
+use proc_macro2::{Span, TokenStream};
+use syn::{parse_quote, Attribute, Expr, FnArg, Ident, ItemFn, Path, ReturnType, Stmt};
+
+use quote::{format_ident, quote};
+
+use crate::utils::attr_ends_with;
+use crate::{
+    parse::{
+        rstest::{RsTestAttributes, RsTestData, RsTestInfo},
+        testcase::TestCase,
+        vlist::ValueList,
+    },
+    utils::attr_is,
+};
+use crate::{
+    refident::MaybeIdent,
+    resolver::{self, Resolver},
+};
+use wrapper::WrapByModule;
+
+pub(crate) use fixture::render as fixture;
+pub(crate) mod inject;
+
+pub(crate) fn single(mut test: ItemFn, info: RsTestInfo) -> TokenStream {
+    let resolver = resolver::fixtures::get(info.data.fixtures());
+    let args = test.sig.inputs.iter().cloned().collect::<Vec<_>>();
+    let attrs = std::mem::take(&mut test.attrs);
+    let asyncness = test.sig.asyncness;
+    let generic_types = test
+        .sig
+        .generics
+        .type_params()
+        .map(|tp| &tp.ident)
+        .cloned()
+        .collect::<Vec<_>>();
+
+    single_test_case(
+        &test.sig.ident,
+        &test.sig.ident,
+        &args,
+        &attrs,
+        &test.sig.output,
+        asyncness,
+        Some(&test),
+        resolver,
+        &info.attributes,
+        &generic_types,
+    )
+}
+
+pub(crate) fn parametrize(test: ItemFn, info: RsTestInfo) -> TokenStream {
+    let RsTestInfo { data, attributes } = info;
+    let resolver_fixtures = resolver::fixtures::get(data.fixtures());
+
+    let rendered_cases = cases_data(&data, test.sig.ident.span())
+        .map(|(name, attrs, resolver)| {
+            TestCaseRender::new(name, attrs, (resolver, &resolver_fixtures))
+        })
+        .map(|case| case.render(&test, &attributes))
+        .collect();
+
+    test_group(test, rendered_cases)
+}
+
+impl ValueList {
+    fn render(
+        &self,
+        test: &ItemFn,
+        resolver: &dyn Resolver,
+        attrs: &[syn::Attribute],
+        attributes: &RsTestAttributes,
+    ) -> TokenStream {
+        let span = test.sig.ident.span();
+        let test_cases = self
+            .argument_data(resolver)
+            .map(|(name, r)| TestCaseRender::new(Ident::new(&name, span), attrs, r))
+            .map(|test_case| test_case.render(test, attributes));
+
+        quote! { #(#test_cases)* }
+    }
+
+    fn argument_data<'a>(
+        &'a self,
+        resolver: &'a dyn Resolver,
+    ) -> impl Iterator<Item = (String, Box<(&'a dyn Resolver, (String, Expr))>)> + 'a {
+        let max_len = self.values.len();
+        self.values.iter().enumerate().map(move |(index, expr)| {
+            let name = format!(
+                "{}_{:0len$}",
+                self.arg,
+                index + 1,
+                len = max_len.display_len()
+            );
+            let resolver_this = (self.arg.to_string(), expr.clone());
+            (name, Box::new((resolver, resolver_this)))
+        })
+    }
+}
+
+fn _matrix_recursive<'a>(
+    test: &ItemFn,
+    list_values: &'a [&'a ValueList],
+    resolver: &dyn Resolver,
+    attrs: &'a [syn::Attribute],
+    attributes: &RsTestAttributes,
+) -> TokenStream {
+    if list_values.is_empty() {
+        return Default::default();
+    }
+    let vlist = list_values[0];
+    let list_values = &list_values[1..];
+
+    if list_values.is_empty() {
+        vlist.render(test, resolver, attrs, attributes)
+    } else {
+        let span = test.sig.ident.span();
+        let modules = vlist.argument_data(resolver).map(move |(name, resolver)| {
+            _matrix_recursive(test, list_values, &resolver, attrs, attributes)
+                .wrap_by_mod(&Ident::new(&name, span))
+        });
+
+        quote! { #(#modules)* }
+    }
+}
+
+pub(crate) fn matrix(test: ItemFn, info: RsTestInfo) -> TokenStream {
+    let RsTestInfo {
+        data, attributes, ..
+    } = info;
+    let span = test.sig.ident.span();
+
+    let cases = cases_data(&data, span).collect::<Vec<_>>();
+
+    let resolver = resolver::fixtures::get(data.fixtures());
+    let rendered_cases = if cases.is_empty() {
+        let list_values = data.list_values().collect::<Vec<_>>();
+        _matrix_recursive(&test, &list_values, &resolver, &[], &attributes)
+    } else {
+        cases
+            .into_iter()
+            .map(|(case_name, attrs, case_resolver)| {
+                let list_values = data.list_values().collect::<Vec<_>>();
+                _matrix_recursive(
+                    &test,
+                    &list_values,
+                    &(case_resolver, &resolver),
+                    attrs,
+                    &attributes,
+                )
+                .wrap_by_mod(&case_name)
+            })
+            .collect()
+    };
+
+    test_group(test, rendered_cases)
+}
+
+fn resolve_default_test_attr(is_async: bool) -> TokenStream {
+    if is_async {
+        quote! { #[async_std::test] }
+    } else {
+        quote! { #[test] }
+    }
+}
+
+fn render_exec_call(fn_path: Path, args: &[Ident], is_async: bool) -> TokenStream {
+    if is_async {
+        quote! {#fn_path(#(#args),*).await}
+    } else {
+        quote! {#fn_path(#(#args),*)}
+    }
+}
+
+/// Render a single test case:
+///
+/// * `name` - Test case name
+/// * `testfn_name` - The name of test function to call
+/// * `args` - The arguments of the test function
+/// * `attrs` - The expected test attributes
+/// * `output` - The expected test return type
+/// * `asyncness` - The `async` fn token
+/// * `test_impl` - If you want embed test function (should be the one called by `testfn_name`)
+/// * `resolver` - The resolver used to resolve injected values
+/// * `attributes` - Test attributes to select test behaviour
+/// * `generic_types` - The genrics type used in signature
+///
+// Ok I need some refactoring here but now that not a real issue
+#[allow(clippy::too_many_arguments)]
+fn single_test_case<'a>(
+    name: &Ident,
+    testfn_name: &Ident,
+    args: &[FnArg],
+    attrs: &[Attribute],
+    output: &ReturnType,
+    asyncness: Option<Async>,
+    test_impl: Option<&ItemFn>,
+    resolver: impl Resolver,
+    attributes: &'a RsTestAttributes,
+    generic_types: &[Ident],
+) -> TokenStream {
+    let (attrs, trace_me): (Vec<_>, Vec<_>) =
+        attrs.iter().cloned().partition(|a| !attr_is(a, "trace"));
+    let mut attributes = attributes.clone();
+    if !trace_me.is_empty() {
+        attributes.add_trace(format_ident!("trace"));
+    }
+    let inject = inject::resolve_aruments(args.iter(), &resolver, generic_types);
+    let args = args
+        .iter()
+        .filter_map(MaybeIdent::maybe_ident)
+        .cloned()
+        .collect::<Vec<_>>();
+    let trace_args = trace_arguments(args.iter(), &attributes);
+
+    let is_async = asyncness.is_some();
+    // If no injected attribut provided use the default one
+    let test_attr = if attrs
+        .iter()
+        .any(|a| attr_ends_with(a, &parse_quote! {test}))
+    {
+        None
+    } else {
+        Some(resolve_default_test_attr(is_async))
+    };
+    let execute = render_exec_call(testfn_name.clone().into(), &args, is_async);
+
+    quote! {
+        #test_attr
+        #(#attrs)*
+        #asyncness fn #name() #output {
+            #test_impl
+            #inject
+            #trace_args
+            println!("{:-^40}", " TEST START ");
+            #execute
+        }
+    }
+}
+
+fn trace_arguments<'a>(
+    args: impl Iterator<Item = &'a Ident>,
+    attributes: &RsTestAttributes,
+) -> Option<TokenStream> {
+    let mut statements = args
+        .filter(|&arg| attributes.trace_me(arg))
+        .map(|arg| {
+            let s: Stmt = parse_quote! {
+                println!("{} = {:?}", stringify!(#arg), #arg);
+            };
+            s
+        })
+        .peekable();
+    if statements.peek().is_some() {
+        Some(quote! {
+            println!("{:-^40}", " TEST ARGUMENTS ");
+            #(#statements)*
+        })
+    } else {
+        None
+    }
+}
+
+struct TestCaseRender<'a> {
+    name: Ident,
+    attrs: &'a [syn::Attribute],
+    resolver: Box<dyn Resolver + 'a>,
+}
+
+impl<'a> TestCaseRender<'a> {
+    pub fn new<R: Resolver + 'a>(name: Ident, attrs: &'a [syn::Attribute], resolver: R) -> Self {
+        TestCaseRender {
+            name,
+            attrs,
+            resolver: Box::new(resolver),
+        }
+    }
+
+    fn render(self, testfn: &ItemFn, attributes: &RsTestAttributes) -> TokenStream {
+        let args = testfn.sig.inputs.iter().cloned().collect::<Vec<_>>();
+        let mut attrs = testfn.attrs.clone();
+        attrs.extend(self.attrs.iter().cloned());
+        let asyncness = testfn.sig.asyncness;
+        let generic_types = testfn
+            .sig
+            .generics
+            .type_params()
+            .map(|tp| &tp.ident)
+            .cloned()
+            .collect::<Vec<_>>();
+
+        single_test_case(
+            &self.name,
+            &testfn.sig.ident,
+            &args,
+            &attrs,
+            &testfn.sig.output,
+            asyncness,
+            None,
+            self.resolver,
+            attributes,
+            &generic_types,
+        )
+    }
+}
+
+fn test_group(mut test: ItemFn, rendered_cases: TokenStream) -> TokenStream {
+    let fname = &test.sig.ident;
+    test.attrs = vec![];
+
+    quote! {
+        #[cfg(test)]
+        #test
+
+        #[cfg(test)]
+        mod #fname {
+            use super::*;
+
+            #rendered_cases
+        }
+    }
+}
+
+trait DisplayLen {
+    fn display_len(&self) -> usize;
+}
+
+impl<D: std::fmt::Display> DisplayLen for D {
+    fn display_len(&self) -> usize {
+        format!("{}", self).len()
+    }
+}
+
+fn format_case_name(case: &TestCase, index: usize, display_len: usize) -> String {
+    let description = case
+        .description
+        .as_ref()
+        .map(|d| format!("_{}", d))
+        .unwrap_or_default();
+    format!(
+        "case_{:0len$}{d}",
+        index,
+        len = display_len,
+        d = description
+    )
+}
+
+fn cases_data(
+    data: &RsTestData,
+    name_span: Span,
+) -> impl Iterator<Item = (Ident, &[syn::Attribute], HashMap<String, &syn::Expr>)> {
+    let display_len = data.cases().count().display_len();
+    data.cases().enumerate().map({
+        move |(n, case)| {
+            let resolver_case = data
+                .case_args()
+                .map(|a| a.to_string())
+                .zip(case.args.iter())
+                .collect::<HashMap<_, _>>();
+            (
+                Ident::new(&format_case_name(case, n + 1, display_len), name_span),
+                case.attrs.as_slice(),
+                resolver_case,
+            )
+        }
+    })
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/render/test.rs b/third_party/rust/rstest/v0_12/crate/src/render/test.rs
new file mode 100644
index 0000000..1beda72
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/render/test.rs
@@ -0,0 +1,1503 @@
+#![cfg(test)]
+
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    parse2, parse_str,
+    visit::Visit,
+    ItemFn, ItemMod,
+};
+
+use super::*;
+use crate::test::{assert_eq, fixture, *};
+use crate::utils::*;
+
+trait SetAsync {
+    fn set_async(&mut self, is_async: bool);
+}
+
+impl SetAsync for ItemFn {
+    fn set_async(&mut self, is_async: bool) {
+        self.sig.asyncness = if is_async {
+            Some(parse_quote! { async })
+        } else {
+            None
+        };
+    }
+}
+
+fn trace_argument_code_string(arg_name: &str) -> String {
+    let arg_name = ident(arg_name);
+    let statment: Stmt = parse_quote! {
+        println!("{} = {:?}", stringify!(#arg_name) ,#arg_name);
+    };
+    statment.display_code()
+}
+
+mod single_test_should {
+    use rstest_test::{assert_in, assert_not_in};
+
+    use crate::test::{assert_eq, *};
+
+    use super::*;
+
+    #[test]
+    fn add_return_type_if_any() {
+        let input_fn: ItemFn = "fn function(fix: String) -> Result<i32, String> { Ok(42) }".ast();
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+
+        assert_eq!(result.sig.output, input_fn.sig.output);
+    }
+
+    #[test]
+    fn include_given_function() {
+        let input_fn: ItemFn = r#"
+                pub fn test<R: AsRef<str>, B>(mut s: String, v: &u32, a: &mut [i32], r: R) -> (u32, B, String, &str)
+                        where B: Borrow<u32>
+                {
+                    let some = 42;
+                    assert_eq!(42, some);
+                }
+                "#.ast();
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+        let first_stmt = result.block.stmts.get(0).unwrap();
+
+        let inner_fn: ItemFn = parse_quote! {
+            #first_stmt
+        };
+
+        assert_eq!(inner_fn, input_fn);
+    }
+
+    #[rstest]
+    fn not_copy_any_attributes(
+        #[values(
+            "#[test]",
+            "#[very::complicated::path]",
+            "#[test]#[should_panic]",
+            "#[should_panic]#[test]",
+            "#[a]#[b]#[c]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let mut input_fn: ItemFn = r#"pub fn test(_s: String){}"#.ast();
+        input_fn.attrs = attributes;
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+        let first_stmt = result.block.stmts.get(0).unwrap();
+
+        let inner_fn: ItemFn = parse_quote! {
+            #first_stmt
+        };
+
+        assert!(inner_fn.attrs.is_empty());
+    }
+
+    #[rstest]
+    #[case::sync(false)]
+    #[case::async_fn(true)]
+    fn use_injected_test_attribute_to_mark_test_functions_if_any(
+        #[case] is_async: bool,
+        #[values(
+            "#[test]",
+            "#[other::test]",
+            "#[very::complicated::path::test]",
+            "#[prev]#[test]",
+            "#[test]#[after]",
+            "#[prev]#[other::test]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let mut input_fn: ItemFn = r#"fn test(_s: String) {} "#.ast();
+        input_fn.set_async(is_async);
+        input_fn.attrs = attributes.clone();
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+
+        assert_eq!(result.attrs, attributes);
+    }
+
+    #[test]
+    fn trace_arguments_values() {
+        let input_fn: ItemFn = r#"#[trace]fn test(s: String, a:i32) {} "#.ast();
+
+        let item_fn: ItemFn = single(input_fn.clone(), Default::default()).ast();
+
+        assert_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("s")
+        );
+        assert_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("a")
+        );
+    }
+
+    #[test]
+    fn trace_not_all_arguments_values() {
+        let input_fn: ItemFn =
+            r#"#[trace] fn test(a_trace: i32, b_no_trace:i32, c_no_trace:i32, d_trace:i32) {} "#
+                .ast();
+
+        let mut attributes = RsTestAttributes::default();
+        attributes.add_notraces(vec![ident("b_no_trace"), ident("c_no_trace")]);
+
+        let item_fn: ItemFn = single(
+            input_fn.clone(),
+            RsTestInfo {
+                attributes,
+                ..Default::default()
+            },
+        )
+        .ast();
+
+        assert_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("a_trace")
+        );
+        assert_not_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("b_no_trace")
+        );
+        assert_not_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("c_no_trace")
+        );
+        assert_in!(
+            item_fn.block.display_code(),
+            trace_argument_code_string("d_trace")
+        );
+    }
+
+    #[rstest]
+    #[case::sync("", parse_quote! { #[test] })]
+    #[case::async_fn("async", parse_quote! { #[async_std::test] })]
+    fn add_default_test_attribute(
+        #[case] prefix: &str,
+        #[case] test_attribute: Attribute,
+        #[values(
+            "",
+            "#[no_one]",
+            "#[should_panic]",
+            "#[should_panic]#[other]",
+            "#[a::b::c]#[should_panic]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let mut input_fn: ItemFn = format!(r#"{} fn test(_s: String) {{}} "#, prefix).ast();
+        input_fn.attrs = attributes.clone();
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+
+        assert_eq!(result.attrs[0], test_attribute);
+        assert_eq!(&result.attrs[1..], attributes.as_slice());
+    }
+
+    #[rstest]
+    #[case::sync(false, false)]
+    #[case::async_fn(true, true)]
+    fn use_await_for_no_async_test_function(#[case] is_async: bool, #[case] use_await: bool) {
+        let mut input_fn: ItemFn = r#"fn test(_s: String) {} "#.ast();
+        input_fn.set_async(is_async);
+
+        let result: ItemFn = single(input_fn.clone(), Default::default()).ast();
+
+        let last_stmt = result.block.stmts.last().unwrap();
+
+        assert_eq!(use_await, last_stmt.is_await());
+    }
+}
+
+struct TestsGroup {
+    requested_test: ItemFn,
+    module: ItemMod,
+}
+
+impl Parse for TestsGroup {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok(Self {
+            requested_test: input.parse()?,
+            module: input.parse()?,
+        })
+    }
+}
+
+trait QueryAttrs {
+    fn has_attr(&self, attr: &syn::Path) -> bool;
+    fn has_attr_that_ends_with(&self, attr: &syn::PathSegment) -> bool;
+}
+
+impl QueryAttrs for ItemFn {
+    fn has_attr(&self, attr: &syn::Path) -> bool {
+        self.attrs.iter().find(|a| &a.path == attr).is_some()
+    }
+
+    fn has_attr_that_ends_with(&self, name: &syn::PathSegment) -> bool {
+        self.attrs
+            .iter()
+            .find(|a| attr_ends_with(a, name))
+            .is_some()
+    }
+}
+
+/// To extract all test functions
+struct TestFunctions(Vec<ItemFn>);
+
+fn is_test_fn(item_fn: &ItemFn) -> bool {
+    item_fn.has_attr_that_ends_with(&parse_quote! { test })
+}
+
+impl TestFunctions {
+    fn is_test_fn(item_fn: &ItemFn) -> bool {
+        is_test_fn(item_fn)
+    }
+}
+
+impl<'ast> Visit<'ast> for TestFunctions {
+    //noinspection RsTypeCheck
+    fn visit_item_fn(&mut self, item_fn: &'ast ItemFn) {
+        if Self::is_test_fn(item_fn) {
+            self.0.push(item_fn.clone())
+        }
+    }
+}
+
+trait Named {
+    fn name(&self) -> String;
+}
+
+impl Named for Ident {
+    fn name(&self) -> String {
+        self.to_string()
+    }
+}
+
+impl Named for ItemFn {
+    fn name(&self) -> String {
+        self.sig.ident.name()
+    }
+}
+
+impl Named for ItemMod {
+    fn name(&self) -> String {
+        self.ident.name()
+    }
+}
+
+trait Names {
+    fn names(&self) -> Vec<String>;
+}
+
+impl<T: Named> Names for Vec<T> {
+    fn names(&self) -> Vec<String> {
+        self.iter().map(Named::name).collect()
+    }
+}
+
+trait ModuleInspector {
+    fn get_all_tests(&self) -> Vec<ItemFn>;
+    fn get_tests(&self) -> Vec<ItemFn>;
+    fn get_modules(&self) -> Vec<ItemMod>;
+}
+
+impl ModuleInspector for ItemMod {
+    fn get_tests(&self) -> Vec<ItemFn> {
+        self.content
+            .as_ref()
+            .map(|(_, items)| {
+                items
+                    .iter()
+                    .filter_map(|it| match it {
+                        syn::Item::Fn(item_fn) if is_test_fn(item_fn) => Some(item_fn.clone()),
+                        _ => None,
+                    })
+                    .collect()
+            })
+            .unwrap_or_default()
+    }
+
+    fn get_all_tests(&self) -> Vec<ItemFn> {
+        let mut f = TestFunctions(vec![]);
+        f.visit_item_mod(&self);
+        f.0
+    }
+
+    fn get_modules(&self) -> Vec<ItemMod> {
+        self.content
+            .as_ref()
+            .map(|(_, items)| {
+                items
+                    .iter()
+                    .filter_map(|it| match it {
+                        syn::Item::Mod(item_mod) => Some(item_mod.clone()),
+                        _ => None,
+                    })
+                    .collect()
+            })
+            .unwrap_or_default()
+    }
+}
+
+impl ModuleInspector for TestsGroup {
+    fn get_all_tests(&self) -> Vec<ItemFn> {
+        self.module.get_all_tests()
+    }
+
+    fn get_tests(&self) -> Vec<ItemFn> {
+        self.module.get_tests()
+    }
+
+    fn get_modules(&self) -> Vec<ItemMod> {
+        self.module.get_modules()
+    }
+}
+
+#[derive(Default, Debug)]
+struct Assignments(HashMap<String, syn::Expr>);
+
+impl<'ast> Visit<'ast> for Assignments {
+    //noinspection RsTypeCheck
+    fn visit_local(&mut self, assign: &syn::Local) {
+        match &assign {
+            syn::Local {
+                pat: syn::Pat::Ident(pat),
+                init: Some((_, expr)),
+                ..
+            } => {
+                self.0.insert(pat.ident.to_string(), expr.as_ref().clone());
+            }
+            _ => {}
+        }
+    }
+}
+
+impl Assignments {
+    pub fn collect_assignments(item_fn: &ItemFn) -> Self {
+        let mut collect = Self::default();
+        collect.visit_item_fn(item_fn);
+        collect
+    }
+}
+
+impl From<TokenStream> for TestsGroup {
+    fn from(tokens: TokenStream) -> Self {
+        syn::parse2::<TestsGroup>(tokens).unwrap()
+    }
+}
+
+mod cases_should {
+    use std::iter::FromIterator;
+
+    use rstest_test::{assert_in, assert_not_in};
+
+    use crate::parse::{
+        rstest::{RsTestData, RsTestInfo, RsTestItem},
+        testcase::TestCase,
+    };
+
+    use super::{assert_eq, *};
+
+    fn into_rstest_data(item_fn: &ItemFn) -> RsTestData {
+        RsTestData {
+            items: fn_args_idents(item_fn)
+                .cloned()
+                .map(RsTestItem::CaseArgName)
+                .collect(),
+        }
+    }
+
+    struct TestCaseBuilder {
+        item_fn: ItemFn,
+        info: RsTestInfo,
+    }
+
+    impl TestCaseBuilder {
+        fn new(item_fn: ItemFn) -> Self {
+            let info: RsTestInfo = into_rstest_data(&item_fn).into();
+            Self { item_fn, info }
+        }
+
+        fn from<S: AsRef<str>>(s: S) -> Self {
+            Self::new(s.as_ref().ast())
+        }
+
+        fn set_async(mut self, is_async: bool) -> Self {
+            self.item_fn.set_async(is_async);
+            self
+        }
+
+        fn push_case<T: Into<TestCase>>(mut self, case: T) -> Self {
+            self.info.push_case(case.into());
+            self
+        }
+
+        fn extend<T: Into<TestCase>>(mut self, cases: impl Iterator<Item = T>) -> Self {
+            self.info.extend(cases.map(Into::into));
+            self
+        }
+
+        fn take(self) -> (ItemFn, RsTestInfo) {
+            (self.item_fn, self.info)
+        }
+
+        fn add_notrace(mut self, idents: Vec<Ident>) -> Self {
+            self.info.attributes.add_notraces(idents);
+            self
+        }
+    }
+
+    fn one_simple_case() -> (ItemFn, RsTestInfo) {
+        TestCaseBuilder::from(r#"fn test(mut fix: String) { println!("user code") }"#)
+            .push_case(r#"String::from("3")"#)
+            .take()
+    }
+
+    fn some_simple_cases(cases: i32) -> (ItemFn, RsTestInfo) {
+        TestCaseBuilder::from(r#"fn test(mut fix: String) { println!("user code") }"#)
+            .extend((0..cases).map(|_| r#"String::from("3")"#))
+            .take()
+    }
+
+    #[test]
+    fn create_a_module_named_as_test_function() {
+        let (item_fn, info) =
+            TestCaseBuilder::from("fn should_be_the_module_name(mut fix: String) {}").take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let output = TestsGroup::from(tokens);
+
+        assert_eq!(output.module.ident, "should_be_the_module_name");
+    }
+
+    #[test]
+    fn copy_user_function() {
+        let (item_fn, info) = TestCaseBuilder::from(
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#,
+        )
+        .take();
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let mut output = TestsGroup::from(tokens);
+
+        output.requested_test.attrs = vec![];
+        assert_eq!(output.requested_test, item_fn);
+    }
+
+    #[test]
+    fn should_not_copy_should_panic_attribute() {
+        let (item_fn, info) = TestCaseBuilder::from(
+            r#"#[should_panic] fn with_should_panic(mut fix: String) { println!("user code") }"#,
+        )
+        .take();
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let output = TestsGroup::from(tokens);
+
+        assert!(!format!("{:?}", output.requested_test.attrs).contains("should_panic"));
+    }
+
+    #[test]
+    fn should_mark_test_with_given_attributes() {
+        let (item_fn, info) =
+            TestCaseBuilder::from(r#"#[should_panic] #[other(value)] fn test(s: String){}"#)
+                .push_case(r#"String::from("3")"#)
+                .take();
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        // Sanity check
+        assert!(tests.len() > 0);
+
+        for t in tests {
+            assert_eq!(item_fn.attrs, &t.attrs[1..]);
+        }
+    }
+
+    #[rstest]
+    #[case::empty("")]
+    #[case::some_attrs("#[a]#[b::c]#[should_panic]")]
+    fn should_add_attributes_given_in_the_test_case(
+        #[case] fnattrs: &str,
+        #[values("", "#[should_panic]", "#[first]#[second(arg)]")] case_attrs: &str,
+    ) {
+        let given_attrs = attrs(fnattrs);
+        let case_attrs = attrs(case_attrs);
+        let (mut item_fn, info) = TestCaseBuilder::from(r#"fn test(v: i32){}"#)
+            .push_case(TestCase::from("42").with_attrs(case_attrs.clone()))
+            .take();
+
+        item_fn.attrs = given_attrs.clone();
+
+        let tokens = parametrize(item_fn, info);
+
+        let test_attrs = &TestsGroup::from(tokens).get_all_tests()[0].attrs[1..];
+
+        let l = given_attrs.len();
+
+        assert_eq!(case_attrs.as_slice(), &test_attrs[l..]);
+        assert_eq!(given_attrs.as_slice(), &test_attrs[..l]);
+    }
+
+    #[test]
+    fn mark_user_function_as_test() {
+        let (item_fn, info) = TestCaseBuilder::from(
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#,
+        )
+        .take();
+        let tokens = parametrize(item_fn, info);
+
+        let output = TestsGroup::from(tokens);
+
+        assert_eq!(
+            output.requested_test.attrs,
+            vec![parse_quote! {#[cfg(test)]}]
+        );
+    }
+
+    #[test]
+    fn mark_module_as_test() {
+        let (item_fn, info) = TestCaseBuilder::from(
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#,
+        )
+        .take();
+        let tokens = parametrize(item_fn, info);
+
+        let output = TestsGroup::from(tokens);
+
+        assert_eq!(output.module.attrs, vec![parse_quote! {#[cfg(test)]}]);
+    }
+
+    #[test]
+    fn add_a_test_case() {
+        let (item_fn, info) = one_simple_case();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert_eq!(1, tests.len());
+        assert!(&tests[0].sig.ident.to_string().starts_with("case_"))
+    }
+
+    #[test]
+    fn add_return_type_if_any() {
+        let (item_fn, info) =
+            TestCaseBuilder::from("fn function(fix: String) -> Result<i32, String> { Ok(42) }")
+                .push_case(r#"String::from("3")"#)
+                .take();
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert_eq!(tests[0].sig.output, item_fn.sig.output);
+    }
+
+    #[test]
+    fn not_copy_user_function() {
+        let t_name = "test_name";
+        let (item_fn, info) = TestCaseBuilder::from(format!(
+            "fn {}(fix: String) -> Result<i32, String> {{ Ok(42) }}",
+            t_name
+        ))
+        .push_case(r#"String::from("3")"#)
+        .take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let test = &TestsGroup::from(tokens).get_all_tests()[0];
+        let inner_functions = extract_inner_functions(&test.block);
+
+        assert_eq!(0, inner_functions.filter(|f| f.sig.ident == t_name).count());
+    }
+
+    #[test]
+    fn starts_case_number_from_1() {
+        let (item_fn, info) = one_simple_case();
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(
+            &tests[0].sig.ident.to_string().starts_with("case_1"),
+            "Should starts with case_1 but is {}",
+            tests[0].sig.ident.to_string()
+        )
+    }
+
+    #[test]
+    fn add_all_test_cases() {
+        let (item_fn, info) = some_simple_cases(5);
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        let valid_names = tests
+            .iter()
+            .filter(|it| it.sig.ident.to_string().starts_with("case_"));
+        assert_eq!(5, valid_names.count())
+    }
+
+    #[test]
+    fn left_pad_case_number_by_zeros() {
+        let (item_fn, info) = some_simple_cases(1000);
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        let first_name = tests[0].sig.ident.to_string();
+        let last_name = tests[999].sig.ident.to_string();
+
+        assert!(
+            first_name.ends_with("_0001"),
+            "Should ends by _0001 but is {}",
+            first_name
+        );
+        assert!(
+            last_name.ends_with("_1000"),
+            "Should ends by _1000 but is {}",
+            last_name
+        );
+
+        let valid_names = tests
+            .iter()
+            .filter(|it| it.sig.ident.to_string().len() == first_name.len());
+        assert_eq!(1000, valid_names.count())
+    }
+
+    #[test]
+    fn use_description_if_any() {
+        let (item_fn, mut info) = one_simple_case();
+        let description = "show_this_description";
+
+        if let &mut RsTestItem::TestCase(ref mut case) = &mut info.data.items[1] {
+            case.description = Some(parse_str::<Ident>(description).unwrap());
+        } else {
+            panic!("Test case should be the second one");
+        }
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(tests[0]
+            .sig
+            .ident
+            .to_string()
+            .ends_with(&format!("_{}", description)));
+    }
+
+    #[rstest]
+    #[case::sync(false)]
+    #[case::async_fn(true)]
+    fn use_injected_test_attribute_to_mark_test_functions_if_any(
+        #[case] is_async: bool,
+        #[values(
+            "#[test]",
+            "#[other::test]",
+            "#[very::complicated::path::test]",
+            "#[prev]#[test]",
+            "#[test]#[after]",
+            "#[prev]#[other::test]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let (mut item_fn, info) = TestCaseBuilder::from(r#"fn test(s: String){}"#)
+            .push_case(r#"String::from("3")"#)
+            .set_async(is_async)
+            .take();
+        item_fn.attrs = attributes.clone();
+        item_fn.set_async(is_async);
+
+        let tokens = parametrize(item_fn.clone(), info);
+
+        let test = &TestsGroup::from(tokens).get_all_tests()[0];
+
+        assert_eq!(attributes, test.attrs);
+    }
+
+    #[rstest]
+    #[case::sync(false, parse_quote! { #[test] })]
+    #[case::async_fn(true, parse_quote! { #[async_std::test] })]
+    fn add_default_test_attribute(
+        #[case] is_async: bool,
+        #[case] test_attribute: Attribute,
+        #[values(
+            "",
+            "#[no_one]",
+            "#[should_panic]",
+            "#[should_panic]#[other]",
+            "#[a::b::c]#[should_panic]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let (mut item_fn, info) = TestCaseBuilder::from(
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#,
+        )
+        .push_case("42")
+        .set_async(is_async)
+        .take();
+        item_fn.attrs = attributes.clone();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert_eq!(tests[0].attrs[0], test_attribute);
+        assert_eq!(&tests[0].attrs[1..], attributes.as_slice());
+    }
+
+    #[rstest]
+    #[case::sync(false, false)]
+    #[case::async_fn(true, true)]
+    fn use_await_for_async_test_function(#[case] is_async: bool, #[case] use_await: bool) {
+        let (item_fn, info) =
+            TestCaseBuilder::from(r#"fn test(mut fix: String) { println!("user code") }"#)
+                .set_async(is_async)
+                .push_case(r#"String::from("3")"#)
+                .take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        let last_stmt = tests[0].block.stmts.last().unwrap();
+
+        assert_eq!(use_await, last_stmt.is_await());
+    }
+
+    #[test]
+    fn trace_arguments_value() {
+        let (item_fn, info) =
+            TestCaseBuilder::from(r#"#[trace] fn test(a_trace_me: i32, b_trace_me: i32) {}"#)
+                .push_case(TestCase::from_iter(vec!["1", "2"]))
+                .push_case(TestCase::from_iter(vec!["3", "4"]))
+                .take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(tests.len() > 0);
+        for test in tests {
+            for name in &["a_trace_me", "b_trace_me"] {
+                assert_in!(test.block.display_code(), trace_argument_code_string(name));
+            }
+        }
+    }
+
+    #[test]
+    fn trace_just_some_arguments_value() {
+        let (item_fn, info) =
+            TestCaseBuilder::from(r#"#[trace] fn test(a_trace_me: i32, b_no_trace_me: i32, c_no_trace_me: i32, d_trace_me: i32) {}"#)
+                .push_case(TestCase::from_iter(vec!["1", "2", "1", "2"]))
+                .push_case(TestCase::from_iter(vec!["3", "4", "3", "4"]))
+                .add_notrace(to_idents!(["b_no_trace_me", "c_no_trace_me"]))
+                .take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(tests.len() > 0);
+        for test in tests {
+            for should_be_present in &["a_trace_me", "d_trace_me"] {
+                assert_in!(
+                    test.block.display_code(),
+                    trace_argument_code_string(should_be_present)
+                );
+            }
+            for should_not_be_present in &["b_trace_me", "c_trace_me"] {
+                assert_not_in!(
+                    test.block.display_code(),
+                    trace_argument_code_string(should_not_be_present)
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn trace_just_one_case() {
+        let (item_fn, info) =
+            TestCaseBuilder::from(r#"fn test(a_no_trace_me: i32, b_trace_me: i32) {}"#)
+                .push_case(TestCase::from_iter(vec!["1", "2"]))
+                .push_case(TestCase::from_iter(vec!["3", "4"]).with_attrs(attrs("#[trace]")))
+                .add_notrace(to_idents!(["a_no_trace_me"]))
+                .take();
+
+        let tokens = parametrize(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert_not_in!(
+            tests[0].block.display_code(),
+            trace_argument_code_string("b_trace_me")
+        );
+        assert_in!(
+            tests[1].block.display_code(),
+            trace_argument_code_string("b_trace_me")
+        );
+        assert_not_in!(
+            tests[1].block.display_code(),
+            trace_argument_code_string("a_no_trace_me")
+        );
+    }
+}
+
+mod matrix_cases_should {
+    use rstest_test::{assert_in, assert_not_in};
+
+    use crate::parse::vlist::ValueList;
+
+    /// Should test matrix tests render without take in account MatrixInfo to RsTestInfo
+    /// transformation
+    use super::{assert_eq, *};
+
+    fn into_rstest_data(item_fn: &ItemFn) -> RsTestData {
+        RsTestData {
+            items: fn_args_idents(item_fn)
+                .cloned()
+                .map(|it| {
+                    ValueList {
+                        arg: it,
+                        values: vec![],
+                    }
+                    .into()
+                })
+                .collect(),
+        }
+    }
+
+    #[test]
+    fn create_a_module_named_as_test_function() {
+        let item_fn = "fn should_be_the_module_name(mut fix: String) {}".ast();
+        let data = into_rstest_data(&item_fn);
+
+        let tokens = matrix(item_fn.clone(), data.into());
+
+        let output = TestsGroup::from(tokens);
+
+        assert_eq!(output.module.ident, "should_be_the_module_name");
+    }
+
+    #[test]
+    fn copy_user_function() {
+        let item_fn =
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#.ast();
+        let data = into_rstest_data(&item_fn);
+
+        let tokens = matrix(item_fn.clone(), data.into());
+
+        let mut output = TestsGroup::from(tokens);
+
+        output.requested_test.attrs = vec![];
+        assert_eq!(output.requested_test, item_fn);
+    }
+
+    #[test]
+    fn not_copy_user_function() {
+        let t_name = "test_name";
+        let item_fn: ItemFn = format!(
+            "fn {}(fix: String) -> Result<i32, String> {{ Ok(42) }}",
+            t_name
+        )
+        .ast();
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![values_list("fix", &["1"]).into()].into(),
+            },
+            ..Default::default()
+        };
+
+        let tokens = matrix(item_fn, info);
+
+        let test = &TestsGroup::from(tokens).get_all_tests()[0];
+        let inner_functions = extract_inner_functions(&test.block);
+
+        assert_eq!(0, inner_functions.filter(|f| f.sig.ident == t_name).count());
+    }
+
+    #[test]
+    fn not_copy_should_panic_attribute() {
+        let item_fn =
+            r#"#[should_panic] fn with_should_panic(mut fix: String) { println!("user code") }"#
+                .ast();
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![values_list("fix", &["1"]).into()].into(),
+            },
+            ..Default::default()
+        };
+
+        let tokens = matrix(item_fn, info);
+
+        let output = TestsGroup::from(tokens);
+
+        assert!(!format!("{:?}", output.requested_test.attrs).contains("should_panic"));
+    }
+
+    #[test]
+    fn should_mark_test_with_given_attributes() {
+        let item_fn: ItemFn = r#"#[should_panic] #[other(value)] fn test(_s: String){}"#.ast();
+
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![values_list("fix", &["1"]).into()].into(),
+            },
+            ..Default::default()
+        };
+        let tokens = matrix(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        // Sanity check
+        assert!(tests.len() > 0);
+
+        for t in tests {
+            assert_eq!(item_fn.attrs, &t.attrs[1..]);
+        }
+    }
+
+    #[test]
+    fn add_return_type_if_any() {
+        let item_fn: ItemFn = "fn function(fix: String) -> Result<i32, String> { Ok(42) }".ast();
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![values_list("fix", &["1", "2", "3"]).into()].into(),
+            },
+            ..Default::default()
+        };
+
+        let tokens = matrix(item_fn.clone(), info);
+
+        let tests = TestsGroup::from(tokens).get_tests();
+
+        assert_eq!(tests[0].sig.output, item_fn.sig.output);
+        assert_eq!(tests[1].sig.output, item_fn.sig.output);
+        assert_eq!(tests[2].sig.output, item_fn.sig.output);
+    }
+
+    #[test]
+    fn mark_user_function_as_test() {
+        let item_fn =
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#.ast();
+        let data = into_rstest_data(&item_fn);
+
+        let tokens = matrix(item_fn.clone(), data.into());
+
+        let output = TestsGroup::from(tokens);
+
+        let expected = parse2::<ItemFn>(quote! {
+            #[cfg(test)]
+            fn some() {}
+        })
+        .unwrap()
+        .attrs;
+
+        assert_eq!(expected, output.requested_test.attrs);
+    }
+
+    #[test]
+    fn mark_module_as_test() {
+        let item_fn =
+            r#"fn should_be_the_module_name(mut fix: String) { println!("user code") }"#.ast();
+        let data = into_rstest_data(&item_fn);
+
+        let tokens = matrix(item_fn.clone(), data.into());
+
+        let output = TestsGroup::from(tokens);
+
+        let expected = parse2::<ItemMod>(quote! {
+            #[cfg(test)]
+            mod some {}
+        })
+        .unwrap()
+        .attrs;
+
+        assert_eq!(expected, output.module.attrs);
+    }
+
+    #[test]
+    fn with_just_one_arg() {
+        let arg_name = "fix";
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![values_list(arg_name, &["1", "2", "3"]).into()].into(),
+            },
+            ..Default::default()
+        };
+
+        let item_fn = format!(r#"fn test({}: u32) {{ println!("user code") }}"#, arg_name).ast();
+
+        let tokens = matrix(item_fn, info);
+
+        let tests = TestsGroup::from(tokens).get_tests();
+
+        assert_eq!(3, tests.len());
+        assert!(&tests[0].sig.ident.to_string().starts_with("fix_"))
+    }
+
+    #[rstest]
+    #[case::sync(false)]
+    #[case::async_fn(true)]
+    fn use_injected_test_attribute_to_mark_test_functions_if_any(
+        #[case] is_async: bool,
+        #[values(
+            "#[test]",
+            "#[other::test]",
+            "#[very::complicated::path::test]",
+            "#[prev]#[test]",
+            "#[test]#[after]",
+            "#[prev]#[other::test]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let data = RsTestData {
+            items: vec![values_list("v", &["1", "2", "3"]).into()].into(),
+        };
+        let mut item_fn: ItemFn = r#"fn test(v: u32) {{ println!("user code") }}"#.ast();
+        item_fn.set_async(is_async);
+        item_fn.attrs = attributes.clone();
+
+        let tokens = matrix(item_fn, data.into());
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        // Sanity check
+        assert!(tests.len() > 0);
+
+        for test in tests {
+            assert_eq!(attributes, test.attrs);
+        }
+    }
+
+    #[rstest]
+    #[case::sync(false, parse_quote! { #[test] })]
+    #[case::async_fn(true, parse_quote! { #[async_std::test] })]
+    fn add_default_test_attribute(
+        #[case] is_async: bool,
+        #[case] test_attribute: Attribute,
+        #[values(
+            "",
+            "#[no_one]",
+            "#[should_panic]",
+            "#[should_panic]#[other]",
+            "#[a::b::c]#[should_panic]"
+        )]
+        attributes: &str,
+    ) {
+        let attributes = attrs(attributes);
+        let data = RsTestData {
+            items: vec![values_list("v", &["1", "2", "3"]).into()].into(),
+        };
+
+        let mut item_fn: ItemFn = r#"fn test(v: u32) {{ println!("user code") }}"#.ast();
+        item_fn.set_async(is_async);
+        item_fn.attrs = attributes.clone();
+
+        let tokens = matrix(item_fn, data.into());
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        // Sanity check
+        assert!(tests.len() > 0);
+
+        for test in tests {
+            assert_eq!(test.attrs[0], test_attribute);
+            assert_eq!(&test.attrs[1..], attributes.as_slice());
+        }
+    }
+
+    #[rstest]
+    #[case::sync(false, false)]
+    #[case::async_fn(true, true)]
+    fn use_await_for_async_test_function(#[case] is_async: bool, #[case] use_await: bool) {
+        let data = RsTestData {
+            items: vec![values_list("v", &["1", "2", "3"]).into()].into(),
+        };
+
+        let mut item_fn: ItemFn = r#"fn test(v: u32) {{ println!("user code") }}"#.ast();
+        item_fn.set_async(is_async);
+
+        let tokens = matrix(item_fn, data.into());
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        // Sanity check
+        assert!(tests.len() > 0);
+
+        for test in tests {
+            let last_stmt = test.block.stmts.last().unwrap();
+            assert_eq!(use_await, last_stmt.is_await());
+        }
+    }
+
+    #[test]
+    fn trace_arguments_value() {
+        let data = RsTestData {
+            items: vec![
+                values_list("a_trace_me", &["1", "2"]).into(),
+                values_list("b_trace_me", &["3", "4"]).into(),
+            ]
+            .into(),
+        };
+        let item_fn: ItemFn = r#"#[trace] fn test(a_trace_me: u32, b_trace_me: u32) {}"#.ast();
+
+        let tokens = matrix(item_fn, data.into());
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(tests.len() > 0);
+        for test in tests {
+            for name in &["a_trace_me", "b_trace_me"] {
+                assert_in!(test.block.display_code(), trace_argument_code_string(name));
+            }
+        }
+    }
+
+    #[test]
+    fn trace_just_some_arguments_value() {
+        let data = RsTestData {
+            items: vec![
+                values_list("a_trace_me", &["1", "2"]).into(),
+                values_list("b_no_trace_me", &["3", "4"]).into(),
+                values_list("c_no_trace_me", &["5", "6"]).into(),
+                values_list("d_trace_me", &["7", "8"]).into(),
+            ]
+            .into(),
+        };
+        let mut attributes: RsTestAttributes = Default::default();
+        attributes.add_notraces(vec![ident("b_no_trace_me"), ident("c_no_trace_me")]);
+        let item_fn: ItemFn = r#"#[trace] fn test(a_trace_me: u32, b_no_trace_me: u32, c_no_trace_me: u32, d_trace_me: u32) {}"#.ast();
+
+        let tokens = matrix(item_fn, RsTestInfo { data, attributes });
+
+        let tests = TestsGroup::from(tokens).get_all_tests();
+
+        assert!(tests.len() > 0);
+        for test in tests {
+            for should_be_present in &["a_trace_me", "d_trace_me"] {
+                assert_in!(
+                    test.block.display_code(),
+                    trace_argument_code_string(should_be_present)
+                );
+            }
+            for should_not_be_present in &["b_no_trace_me", "c_no_trace_me"] {
+                assert_not_in!(
+                    test.block.display_code(),
+                    trace_argument_code_string(should_not_be_present)
+                );
+            }
+        }
+    }
+
+    mod two_args_should {
+        /// Should test matrix tests render without take in account MatrixInfo to RsTestInfo
+        /// transformation
+        use super::{assert_eq, *};
+
+        fn fixture<'a>() -> (Vec<&'a str>, ItemFn, RsTestInfo) {
+            let names = vec!["first", "second"];
+            (
+                names.clone(),
+                format!(
+                    r#"fn test({}: u32, {}: u32) {{ println!("user code") }}"#,
+                    names[0], names[1]
+                )
+                .ast(),
+                RsTestInfo {
+                    data: RsTestData {
+                        items: vec![
+                            values_list(names[0], &["1", "2", "3"]).into(),
+                            values_list(names[1], &["1", "2"]).into(),
+                        ],
+                    },
+                    ..Default::default()
+                },
+            )
+        }
+
+        #[test]
+        fn contain_a_module_for_each_first_arg() {
+            let (names, item_fn, info) = fixture();
+
+            let tokens = matrix(item_fn, info);
+
+            let modules = TestsGroup::from(tokens).module.get_modules().names();
+
+            let expected = (1..=3)
+                .map(|i| format!("{}_{}", names[0], i))
+                .collect::<Vec<_>>();
+
+            assert_eq!(expected, modules);
+        }
+
+        #[test]
+        fn create_all_tests() {
+            let (_, item_fn, info) = fixture();
+
+            let tokens = matrix(item_fn, info);
+
+            let tests = TestsGroup::from(tokens).module.get_all_tests().names();
+
+            assert_eq!(6, tests.len());
+        }
+
+        #[test]
+        fn create_all_modules_with_the_same_functions() {
+            let (_, item_fn, info) = fixture();
+
+            let tokens = matrix(item_fn, info);
+
+            let tests = TestsGroup::from(tokens)
+                .module
+                .get_modules()
+                .into_iter()
+                .map(|m| m.get_tests().names())
+                .collect::<Vec<_>>();
+
+            assert_eq!(tests[0], tests[1]);
+            assert_eq!(tests[1], tests[2]);
+        }
+
+        #[test]
+        fn test_name_should_contain_argument_name() {
+            let (names, item_fn, info) = fixture();
+
+            let tokens = matrix(item_fn, info);
+
+            let tests = TestsGroup::from(tokens).module.get_modules()[0]
+                .get_tests()
+                .names();
+
+            let expected = (1..=2)
+                .map(|i| format!("{}_{}", names[1], i))
+                .collect::<Vec<_>>();
+
+            assert_eq!(expected, tests);
+        }
+    }
+
+    #[test]
+    fn three_args_should_create_all_function_4_mods_at_the_first_level_and_3_at_the_second() {
+        let (first, second, third) = ("first", "second", "third");
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![
+                    values_list(first, &["1", "2", "3", "4"]).into(),
+                    values_list(second, &["1", "2", "3"]).into(),
+                    values_list(third, &["1", "2"]).into(),
+                ],
+            },
+            ..Default::default()
+        };
+        let item_fn = format!(
+            r#"fn test({}: u32, {}: u32, {}: u32) {{ println!("user code") }}"#,
+            first, second, third
+        )
+        .ast();
+
+        let tokens = matrix(item_fn, info);
+
+        let tg = TestsGroup::from(tokens);
+
+        assert_eq!(24, tg.module.get_all_tests().len());
+        assert_eq!(4, tg.module.get_modules().len());
+        assert_eq!(3, tg.module.get_modules()[0].get_modules().len());
+        assert_eq!(3, tg.module.get_modules()[3].get_modules().len());
+        assert_eq!(
+            2,
+            tg.module.get_modules()[0].get_modules()[0]
+                .get_tests()
+                .len()
+        );
+        assert_eq!(
+            2,
+            tg.module.get_modules()[3].get_modules()[1]
+                .get_tests()
+                .len()
+        );
+    }
+
+    #[test]
+    fn pad_case_index() {
+        let item_fn: ItemFn =
+            r#"fn test(first: u32, second: u32, third: u32) { println!("user code") }"#.ast();
+        let values = (1..=100).map(|i| i.to_string()).collect::<Vec<_>>();
+        let info = RsTestInfo {
+            data: RsTestData {
+                items: vec![
+                    values_list("first", values.as_ref()).into(),
+                    values_list("second", values[..10].as_ref()).into(),
+                    values_list("third", values[..2].as_ref()).into(),
+                ],
+            },
+            ..Default::default()
+        };
+
+        let tokens = matrix(item_fn.clone(), info);
+
+        let tg = TestsGroup::from(tokens);
+
+        let mods = tg.get_modules().names();
+
+        assert_eq!(mods[0], "first_001");
+        assert_eq!(mods[99], "first_100");
+
+        let mods = tg.get_modules()[0].get_modules().names();
+
+        assert_eq!(mods[0], "second_01");
+        assert_eq!(mods[9], "second_10");
+
+        let functions = tg.get_modules()[0].get_modules()[1].get_tests().names();
+
+        assert_eq!(functions[0], "third_1");
+        assert_eq!(functions[1], "third_2");
+    }
+}
+
+mod complete_should {
+    use super::{assert_eq, *};
+
+    fn rendered_case(fn_name: &str) -> TestsGroup {
+        let item_fn: ItemFn = format!(
+            r#"         #[first]
+                        #[second(arg)]
+                        fn {}(
+                            fix: u32,
+                            a: f64, b: f32,
+                            x: i32, y: i32) {{}}"#,
+            fn_name
+        )
+        .ast();
+        let data = RsTestData {
+            items: vec![
+                fixture("fix", &["2"]).into(),
+                ident("a").into(),
+                ident("b").into(),
+                vec!["1f64", "2f32"]
+                    .into_iter()
+                    .collect::<TestCase>()
+                    .into(),
+                TestCase {
+                    description: Some(ident("description")),
+                    ..vec!["3f64", "4f32"].into_iter().collect::<TestCase>()
+                }
+                .with_attrs(attrs("#[third]#[forth(other)]"))
+                .into(),
+                values_list("x", &["12", "-2"]).into(),
+                values_list("y", &["-3", "42"]).into(),
+            ],
+        };
+
+        matrix(item_fn.clone(), data.into()).into()
+    }
+
+    fn test_case() -> TestsGroup {
+        rendered_case("test_function")
+    }
+
+    #[test]
+    fn use_function_name_as_outer_module() {
+        let rendered = rendered_case("should_be_the_outer_module_name");
+
+        assert_eq!(rendered.module.ident, "should_be_the_outer_module_name")
+    }
+
+    #[test]
+    fn have_one_module_for_each_parametrized_case() {
+        let rendered = test_case();
+
+        assert_eq!(
+            vec!["case_1", "case_2_description"],
+            rendered
+                .get_modules()
+                .iter()
+                .map(|m| m.ident.to_string())
+                .collect::<Vec<_>>()
+        );
+    }
+
+    #[test]
+    fn implement_exactly_8_tests() {
+        let rendered = test_case();
+
+        assert_eq!(8, rendered.get_all_tests().len());
+    }
+
+    #[test]
+    fn implement_exactly_4_tests_in_each_module() {
+        let modules = test_case().module.get_modules();
+
+        assert_eq!(4, modules[0].get_all_tests().len());
+        assert_eq!(4, modules[1].get_all_tests().len());
+    }
+
+    #[test]
+    fn assign_same_case_value_for_each_test() {
+        let modules = test_case().module.get_modules();
+
+        for f in modules[0].get_all_tests() {
+            let assignments = Assignments::collect_assignments(&f);
+            assert_eq!(assignments.0["a"], expr("1f64"));
+            assert_eq!(assignments.0["b"], expr("2f32"));
+        }
+
+        for f in modules[1].get_all_tests() {
+            let assignments = Assignments::collect_assignments(&f);
+            assert_eq!(assignments.0["a"], expr("3f64"));
+            assert_eq!(assignments.0["b"], expr("4f32"));
+        }
+    }
+
+    #[test]
+    fn assign_all_case_combination_in_tests() {
+        let modules = test_case().module.get_modules();
+
+        let cases = vec![("12", "-3"), ("12", "42"), ("-2", "-3"), ("-2", "42")];
+        for module in modules {
+            for ((x, y), f) in cases.iter().zip(module.get_all_tests().iter()) {
+                let assignments = Assignments::collect_assignments(f);
+                assert_eq!(assignments.0["x"], expr(x));
+                assert_eq!(assignments.0["y"], expr(y));
+            }
+        }
+    }
+
+    #[test]
+    fn mark_test_with_given_attributes() {
+        let modules = test_case().module.get_modules();
+        let attrs = attrs("#[first]#[second(arg)]");
+
+        for f in modules[0].get_all_tests() {
+            assert_eq!(attrs, &f.attrs[1..]);
+        }
+        for f in modules[1].get_all_tests() {
+            assert_eq!(attrs, &f.attrs[1..3]);
+        }
+    }
+    #[test]
+    fn should_add_attributes_given_in_the_test_case() {
+        let modules = test_case().module.get_modules();
+        let attrs = attrs("#[third]#[forth(other)]");
+
+        for f in modules[1].get_all_tests() {
+            assert_eq!(attrs, &f.attrs[3..]);
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/render/wrapper.rs b/third_party/rust/rstest/v0_12/crate/src/render/wrapper.rs
new file mode 100644
index 0000000..a513a7c6
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/render/wrapper.rs
@@ -0,0 +1,19 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::Ident;
+
+pub(crate) trait WrapByModule {
+    fn wrap_by_mod(&self, mod_name: &Ident) -> TokenStream;
+}
+
+impl<T: ToTokens> WrapByModule for T {
+    fn wrap_by_mod(&self, mod_name: &Ident) -> TokenStream {
+        quote! {
+            mod #mod_name {
+                use super::*;
+
+                #self
+            }
+        }
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/resolver.rs b/third_party/rust/rstest/v0_12/crate/src/resolver.rs
new file mode 100644
index 0000000..8267abf
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/resolver.rs
@@ -0,0 +1,174 @@
+/// Define `Resolver` trait and implement it on some hashmaps and also define the `Resolver` tuple
+/// composition. Provide also some utility functions related to how to create a `Resolver` and
+/// resolving render.
+///
+use std::borrow::Cow;
+use std::collections::HashMap;
+
+use proc_macro2::Ident;
+use syn::{parse_quote, Expr};
+
+use crate::parse::Fixture;
+
+pub(crate) mod fixtures {
+    use quote::format_ident;
+
+    use super::*;
+
+    pub(crate) fn get<'a>(fixtures: impl Iterator<Item = &'a Fixture>) -> impl Resolver + 'a {
+        fixtures
+            .map(|f| (f.name.to_string(), extract_resolve_expression(f)))
+            .collect::<HashMap<_, Expr>>()
+    }
+
+    fn extract_resolve_expression(fixture: &Fixture) -> syn::Expr {
+        let resolve = fixture.resolve.as_ref().unwrap_or(&fixture.name);
+        let positional = &fixture.positional.0;
+        let f_name = match positional.len() {
+            0 => format_ident!("default"),
+            l => format_ident!("partial_{}", l),
+        };
+        parse_quote! { #resolve::#f_name(#(#positional), *) }
+    }
+
+    #[cfg(test)]
+    mod should {
+        use super::*;
+        use crate::test::{assert_eq, *};
+
+        #[rstest]
+        #[case(&[], "default()")]
+        #[case(&["my_expression"], "partial_1(my_expression)")]
+        #[case(&["first", "other"], "partial_2(first, other)")]
+        fn resolve_by_use_the_given_name(#[case] args: &[&str], #[case] expected: &str) {
+            let data = vec![fixture("pippo", args)];
+            let resolver = get(data.iter());
+
+            let resolved = resolver.resolve(&ident("pippo")).unwrap().into_owned();
+
+            assert_eq!(resolved, format!("pippo::{}", expected).ast());
+        }
+
+        #[rstest]
+        #[case(&[], "default()")]
+        #[case(&["my_expression"], "partial_1(my_expression)")]
+        #[case(&["first", "other"], "partial_2(first, other)")]
+        fn resolve_by_use_the_resolve_field(#[case] args: &[&str], #[case] expected: &str) {
+            let data = vec![fixture("pippo", args).with_resolve("pluto")];
+            let resolver = get(data.iter());
+
+            let resolved = resolver.resolve(&ident("pippo")).unwrap().into_owned();
+
+            assert_eq!(resolved, format!("pluto::{}", expected).ast());
+        }
+    }
+}
+
+pub(crate) mod values {
+    use super::*;
+    use crate::parse::fixture::ArgumentValue;
+
+    pub(crate) fn get<'a>(values: impl Iterator<Item = &'a ArgumentValue>) -> impl Resolver + 'a {
+        values
+            .map(|av| (av.name.to_string(), &av.expr))
+            .collect::<HashMap<_, &'a Expr>>()
+    }
+
+    #[cfg(test)]
+    mod should {
+        use super::*;
+        use crate::test::{assert_eq, *};
+
+        #[test]
+        fn resolve_by_use_the_given_name() {
+            let data = vec![
+                arg_value("pippo", "42"),
+                arg_value("donaldduck", "vec![1,2]"),
+            ];
+            let resolver = get(data.iter());
+
+            assert_eq!(
+                resolver.resolve(&ident("pippo")).unwrap().into_owned(),
+                "42".ast()
+            );
+            assert_eq!(
+                resolver.resolve(&ident("donaldduck")).unwrap().into_owned(),
+                "vec![1,2]".ast()
+            );
+        }
+    }
+}
+
+/// A trait that `resolve` the given ident to expression code to assign the value.
+pub(crate) trait Resolver {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>>;
+}
+
+impl<'a> Resolver for HashMap<String, &'a Expr> {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        let ident = ident.to_string();
+        self.get(&ident).map(|&c| Cow::Borrowed(c))
+    }
+}
+
+impl<'a> Resolver for HashMap<String, Expr> {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        let ident = ident.to_string();
+        self.get(&ident).map(Cow::Borrowed)
+    }
+}
+
+impl<R1: Resolver, R2: Resolver> Resolver for (R1, R2) {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        self.0.resolve(ident).or_else(|| self.1.resolve(ident))
+    }
+}
+
+impl<R: Resolver + ?Sized> Resolver for &R {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        (*self).resolve(ident)
+    }
+}
+
+impl<R: Resolver + ?Sized> Resolver for Box<R> {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        (**self).resolve(ident)
+    }
+}
+
+impl Resolver for (String, Expr) {
+    fn resolve(&self, ident: &Ident) -> Option<Cow<Expr>> {
+        if *ident == self.0 {
+            Some(Cow::Borrowed(&self.1))
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod should {
+    use super::*;
+    use crate::test::{assert_eq, *};
+    use syn::parse_str;
+
+    #[test]
+    fn return_the_given_expression() {
+        let ast = parse_str("fn function(mut foo: String) {}").unwrap();
+        let arg = first_arg_ident(&ast);
+        let expected = expr("bar()");
+        let mut resolver = HashMap::new();
+
+        resolver.insert("foo".to_string(), &expected);
+
+        assert_eq!(expected, (&resolver).resolve(&arg).unwrap().into_owned())
+    }
+
+    #[test]
+    fn return_none_for_unknown_argument() {
+        let ast = "fn function(mut fix: String) {}".ast();
+        let arg = first_arg_ident(&ast);
+
+        assert!(EmptyResolver.resolve(&arg).is_none())
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/test.rs b/third_party/rust/rstest/v0_12/crate/src/test.rs
new file mode 100644
index 0000000..61f0537
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/test.rs
@@ -0,0 +1,320 @@
+#![macro_use]
+
+/// Unit testing utility module. Collect a bunch of functions&macro and impls to simplify unit
+/// testing bolilerplate.
+///
+use std::borrow::Cow;
+use std::iter::FromIterator;
+
+pub(crate) use mytest::{fixture, rstest};
+pub(crate) use pretty_assertions::assert_eq;
+use proc_macro2::TokenTree;
+use quote::quote;
+use syn::{parse::Parse, parse2, parse_str, Error, Expr, Ident, ItemFn, Stmt};
+
+use super::*;
+use crate::parse::{
+    fixture::{FixtureData, FixtureItem},
+    rstest::{RsTestData, RsTestItem},
+    testcase::TestCase,
+    vlist::ValueList,
+    Attribute, Fixture, Positional,
+};
+use crate::resolver::Resolver;
+use crate::utils::fn_args_idents;
+use parse::fixture::ArgumentValue;
+
+macro_rules! to_args {
+    ($e:expr) => {{
+        $e.iter()
+            .map(|s| s as &dyn AsRef<str>)
+            .map(expr)
+            .collect::<Vec<_>>()
+    }};
+}
+
+macro_rules! to_exprs {
+    ($e:expr) => {
+        $e.iter().map(|s| expr(s)).collect::<Vec<_>>()
+    };
+}
+
+macro_rules! to_strs {
+    ($e:expr) => {
+        $e.iter().map(ToString::to_string).collect::<Vec<_>>()
+    };
+}
+
+macro_rules! to_idents {
+    ($e:expr) => {
+        $e.iter().map(|s| ident(s)).collect::<Vec<_>>()
+    };
+}
+
+struct Outer<T>(T);
+impl<T: Parse> Parse for Outer<T> {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let outer: Ident = input.parse()?;
+        if outer == "outer" {
+            let content;
+            let _ = syn::parenthesized!(content in input);
+            content.parse().map(Outer)
+        } else {
+            Err(Error::new(outer.span(), "Expected 'outer'"))
+        }
+    }
+}
+
+pub(crate) fn parse_meta<T: syn::parse::Parse, S: AsRef<str>>(test_case: S) -> T {
+    let to_parse = format!(
+        r#"
+        #[outer({})]
+        fn to_parse() {{}}
+        "#,
+        test_case.as_ref()
+    );
+
+    let item_fn = parse_str::<ItemFn>(&to_parse).expect(&format!("Cannot parse '{}'", to_parse));
+
+    let tokens = quote!(
+        #item_fn
+    );
+
+    let tt = tokens.into_iter().skip(1).next().unwrap();
+
+    if let TokenTree::Group(g) = tt {
+        let ts = g.stream();
+        parse2::<Outer<T>>(ts).unwrap().0
+    } else {
+        panic!("Cannot find group in {:#?}", tt)
+    }
+}
+
+pub(crate) trait ToAst {
+    fn ast<T: Parse>(self) -> T;
+}
+
+impl ToAst for &str {
+    fn ast<T: Parse>(self) -> T {
+        parse_str(self).unwrap()
+    }
+}
+
+impl ToAst for String {
+    fn ast<T: Parse>(self) -> T {
+        parse_str(&self).unwrap()
+    }
+}
+
+impl ToAst for proc_macro2::TokenStream {
+    fn ast<T: Parse>(self) -> T {
+        parse2(self).unwrap()
+    }
+}
+
+pub(crate) fn ident(s: impl AsRef<str>) -> syn::Ident {
+    s.as_ref().ast()
+}
+
+pub(crate) fn expr(s: impl AsRef<str>) -> syn::Expr {
+    s.as_ref().ast()
+}
+
+pub(crate) fn attrs(s: impl AsRef<str>) -> Vec<syn::Attribute> {
+    parse_str::<ItemFn>(&format!(
+        r#"{}
+           fn _no_name_() {{}}   
+        "#,
+        s.as_ref()
+    ))
+    .unwrap()
+    .attrs
+}
+
+pub(crate) fn fixture(name: impl AsRef<str>, args: &[&str]) -> Fixture {
+    Fixture::new(ident(name), None, Positional(to_exprs!(args)))
+}
+
+pub(crate) fn arg_value(name: impl AsRef<str>, value: impl AsRef<str>) -> ArgumentValue {
+    ArgumentValue::new(ident(name), expr(value))
+}
+
+pub(crate) fn values_list<S: AsRef<str>>(arg: &str, values: &[S]) -> ValueList {
+    ValueList {
+        arg: ident(arg),
+        values: values.into_iter().map(|s| expr(s)).collect(),
+    }
+}
+
+pub(crate) fn first_arg_ident(ast: &ItemFn) -> &Ident {
+    fn_args_idents(&ast).next().unwrap()
+}
+
+pub(crate) fn extract_inner_functions(block: &syn::Block) -> impl Iterator<Item = &syn::ItemFn> {
+    block.stmts.iter().filter_map(|s| match s {
+        syn::Stmt::Item(syn::Item::Fn(f)) => Some(f),
+        _ => None,
+    })
+}
+
+pub(crate) fn literal_expressions_str() -> Vec<&'static str> {
+    vec![
+        "42",
+        "42isize",
+        "1.0",
+        "-1",
+        "-1.0",
+        "true",
+        "1_000_000u64",
+        "0b10100101u8",
+        r#""42""#,
+        "b'H'",
+    ]
+}
+
+pub(crate) trait ExtractArgs {
+    fn args(&self) -> Vec<Expr>;
+}
+
+impl ExtractArgs for TestCase {
+    fn args(&self) -> Vec<Expr> {
+        self.args.iter().cloned().collect()
+    }
+}
+
+impl ExtractArgs for ValueList {
+    fn args(&self) -> Vec<Expr> {
+        self.values.iter().cloned().collect()
+    }
+}
+
+impl Attribute {
+    pub fn attr<S: AsRef<str>>(s: S) -> Self {
+        Attribute::Attr(ident(s))
+    }
+
+    pub fn tagged<SI: AsRef<str>, SA: AsRef<str>>(tag: SI, attrs: Vec<SA>) -> Self {
+        Attribute::Tagged(ident(tag), attrs.into_iter().map(|a| ident(a)).collect())
+    }
+
+    pub fn typed<S: AsRef<str>, T: AsRef<str>>(tag: S, inner: T) -> Self {
+        Attribute::Type(ident(tag), parse_str(inner.as_ref()).unwrap())
+    }
+}
+
+impl RsTestInfo {
+    pub fn push_case(&mut self, case: TestCase) {
+        self.data.items.push(RsTestItem::TestCase(case));
+    }
+
+    pub fn extend(&mut self, cases: impl Iterator<Item = TestCase>) {
+        self.data.items.extend(cases.map(RsTestItem::TestCase));
+    }
+}
+
+impl Fixture {
+    pub fn with_resolve(mut self, resolve_ident: &str) -> Self {
+        self.resolve = Some(ident(resolve_ident));
+        self
+    }
+}
+
+impl TestCase {
+    pub fn with_description(mut self, description: &str) -> Self {
+        self.description = Some(ident(description));
+        self
+    }
+
+    pub fn with_attrs(mut self, attrs: Vec<syn::Attribute>) -> Self {
+        self.attrs = attrs;
+        self
+    }
+}
+
+impl<A: AsRef<str>> FromIterator<A> for TestCase {
+    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
+        TestCase {
+            args: iter.into_iter().map(expr).collect(),
+            attrs: Default::default(),
+            description: None,
+        }
+    }
+}
+
+impl<'a> From<&'a str> for TestCase {
+    fn from(argument: &'a str) -> Self {
+        std::iter::once(argument).collect()
+    }
+}
+
+impl From<Vec<RsTestItem>> for RsTestData {
+    fn from(items: Vec<RsTestItem>) -> Self {
+        Self { items }
+    }
+}
+
+impl From<RsTestData> for RsTestInfo {
+    fn from(data: RsTestData) -> Self {
+        Self {
+            data,
+            attributes: Default::default(),
+        }
+    }
+}
+
+impl From<Vec<Expr>> for Positional {
+    fn from(data: Vec<Expr>) -> Self {
+        Positional(data)
+    }
+}
+
+impl From<Vec<FixtureItem>> for FixtureData {
+    fn from(fixtures: Vec<FixtureItem>) -> Self {
+        Self { items: fixtures }
+    }
+}
+
+pub(crate) struct EmptyResolver;
+
+impl<'a> Resolver for EmptyResolver {
+    fn resolve(&self, _ident: &Ident) -> Option<Cow<Expr>> {
+        None
+    }
+}
+
+pub(crate) trait IsAwait {
+    fn is_await(&self) -> bool;
+}
+
+impl IsAwait for Stmt {
+    fn is_await(&self) -> bool {
+        match self {
+            Stmt::Expr(Expr::Await(_)) => true,
+            _ => false,
+        }
+    }
+}
+
+pub(crate) trait DisplayCode {
+    fn display_code(&self) -> String;
+}
+
+impl<T: ToTokens> DisplayCode for T {
+    fn display_code(&self) -> String {
+        self.to_token_stream().to_string()
+    }
+}
+
+impl crate::parse::fixture::FixtureInfo {
+    pub(crate) fn with_once(mut self) -> Self {
+        self.attributes = self.attributes.with_once();
+        self
+    }
+}
+
+impl crate::parse::fixture::FixtureModifiers {
+    pub(crate) fn with_once(mut self) -> Self {
+        self.append(Attribute::attr("once"));
+        self
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/src/utils.rs b/third_party/rust/rstest/v0_12/crate/src/utils.rs
new file mode 100644
index 0000000..459d204d
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/src/utils.rs
@@ -0,0 +1,391 @@
+/// Contains some unsorted functions used across others modules
+///
+use quote::format_ident;
+use std::collections::{HashMap, HashSet};
+
+use crate::refident::MaybeIdent;
+use syn::{Attribute, Expr, FnArg, Generics, Ident, ItemFn, ReturnType, Type, WherePredicate};
+
+/// Return an iterator over fn arguments items.
+///
+pub(crate) fn fn_args_idents(test: &ItemFn) -> impl Iterator<Item = &Ident> {
+    fn_args(test).filter_map(MaybeIdent::maybe_ident)
+}
+
+/// Return if function declaration has an ident
+///
+pub(crate) fn fn_args_has_ident(fn_decl: &ItemFn, ident: &Ident) -> bool {
+    fn_args_idents(fn_decl).any(|id| id == ident)
+}
+
+/// Return an iterator over fn arguments.
+///
+pub(crate) fn fn_args(item_fn: &ItemFn) -> impl Iterator<Item = &FnArg> {
+    item_fn.sig.inputs.iter()
+}
+
+pub(crate) fn attr_ends_with(attr: &Attribute, segment: &syn::PathSegment) -> bool {
+    attr.path.segments.iter().last() == Some(segment)
+}
+
+pub(crate) fn attr_starts_with(attr: &Attribute, segment: &syn::PathSegment) -> bool {
+    attr.path.segments.iter().next() == Some(segment)
+}
+
+pub(crate) fn attr_is(attr: &Attribute, name: &str) -> bool {
+    attr.path.is_ident(&format_ident!("{}", name))
+}
+
+pub(crate) fn attr_in(attr: &Attribute, names: &[&str]) -> bool {
+    names
+        .iter()
+        .any(|name| attr.path.is_ident(&format_ident!("{}", name)))
+}
+
+pub(crate) trait IsLiteralExpression {
+    fn is_literal(&self) -> bool;
+}
+
+impl<E: AsRef<Expr>> IsLiteralExpression for E {
+    fn is_literal(&self) -> bool {
+        matches!(
+            self.as_ref(),
+            Expr::Lit(syn::ExprLit {
+                lit: syn::Lit::Str(_),
+                ..
+            })
+        )
+    }
+}
+
+// Recoursive search id by reference till find one in ends
+fn _is_used(
+    visited: &mut HashSet<Ident>,
+    id: &Ident,
+    references: &HashMap<Ident, HashSet<Ident>>,
+    ends: &HashSet<Ident>,
+) -> bool {
+    if visited.contains(id) {
+        return false;
+    }
+    visited.insert(id.clone());
+    if ends.contains(id) {
+        return true;
+    }
+    if references.contains_key(id) {
+        for refered in references.get(id).unwrap() {
+            if _is_used(visited, refered, references, ends) {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+// Recoursive search id by reference till find one in ends
+fn is_used(id: &Ident, references: &HashMap<Ident, HashSet<Ident>>, ends: &HashSet<Ident>) -> bool {
+    let mut visited = Default::default();
+    _is_used(&mut visited, id, references, ends)
+}
+
+impl MaybeIdent for syn::WherePredicate {
+    fn maybe_ident(&self) -> Option<&Ident> {
+        match self {
+            WherePredicate::Type(syn::PredicateType { bounded_ty: t, .. }) => {
+                first_type_path_segment_ident(t)
+            }
+            WherePredicate::Lifetime(syn::PredicateLifetime { lifetime, .. }) => {
+                Some(&lifetime.ident)
+            }
+            WherePredicate::Eq(_) => None,
+        }
+    }
+}
+
+#[derive(Default)]
+struct SearchSimpleTypeName(HashSet<Ident>);
+
+impl SearchSimpleTypeName {
+    fn take(self) -> HashSet<Ident> {
+        self.0
+    }
+
+    fn visit_inputs<'a>(&mut self, inputs: impl Iterator<Item = &'a FnArg>) {
+        use syn::visit::Visit;
+        inputs.for_each(|fn_arg| self.visit_fn_arg(fn_arg));
+    }
+    fn visit_output(&mut self, output: &ReturnType) {
+        use syn::visit::Visit;
+        self.visit_return_type(output);
+    }
+
+    fn collect_from_type_param(tp: &syn::TypeParam) -> Self {
+        let mut s: Self = Default::default();
+        use syn::visit::Visit;
+        s.visit_type_param(tp);
+        s
+    }
+
+    fn collect_from_where_predicate(wp: &syn::WherePredicate) -> Self {
+        let mut s: Self = Default::default();
+        use syn::visit::Visit;
+        s.visit_where_predicate(wp);
+        s
+    }
+}
+
+impl<'ast> syn::visit::Visit<'ast> for SearchSimpleTypeName {
+    fn visit_path(&mut self, p: &'ast syn::Path) {
+        if let Some(id) = p.get_ident() {
+            self.0.insert(id.clone());
+        }
+        syn::visit::visit_path(self, p)
+    }
+
+    fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) {
+        self.0.insert(i.ident.clone());
+        syn::visit::visit_lifetime(self, i)
+    }
+}
+
+// Take generics definitions and where clauses and return the
+// a map from simple types (lifetime names or type with just names)
+// to a set of all simple types that use it as some costrain.
+fn extract_references_map(generics: &Generics) -> HashMap<Ident, HashSet<Ident>> {
+    let mut references = HashMap::<Ident, HashSet<Ident>>::default();
+    // Extracts references from types param
+    generics.type_params().for_each(|tp| {
+        SearchSimpleTypeName::collect_from_type_param(tp)
+            .take()
+            .into_iter()
+            .for_each(|id| {
+                references.entry(id).or_default().insert(tp.ident.clone());
+            });
+    });
+    // Extracts references from where clauses
+    generics
+        .where_clause
+        .iter()
+        .flat_map(|wc| wc.predicates.iter())
+        .filter_map(|wp| wp.maybe_ident().map(|id| (id, wp)))
+        .for_each(|(ref_ident, wp)| {
+            SearchSimpleTypeName::collect_from_where_predicate(wp)
+                .take()
+                .into_iter()
+                .for_each(|id| {
+                    references.entry(id).or_default().insert(ref_ident.clone());
+                });
+        });
+    references
+}
+
+// Return a hash set that contains all types and lifetimes referenced
+// in input/output expressed by a single ident.
+fn references_ident_types<'a>(
+    generics: &Generics,
+    inputs: impl Iterator<Item = &'a FnArg>,
+    output: &ReturnType,
+) -> HashSet<Ident> {
+    let mut used: SearchSimpleTypeName = Default::default();
+    used.visit_output(output);
+    used.visit_inputs(inputs);
+    let references = extract_references_map(generics);
+    let mut used = used.take();
+    let input_output = used.clone();
+    // Extend the input output collected ref with the transitive ones:
+    used.extend(
+        generics
+            .params
+            .iter()
+            .filter_map(MaybeIdent::maybe_ident)
+            .filter(|&id| is_used(id, &references, &input_output))
+            .cloned(),
+    );
+    used
+}
+
+fn filtered_predicates(mut wc: syn::WhereClause, valids: &HashSet<Ident>) -> syn::WhereClause {
+    wc.predicates = wc
+        .predicates
+        .clone()
+        .into_iter()
+        .filter(|wp| {
+            wp.maybe_ident()
+                .map(|t| valids.contains(t))
+                .unwrap_or_default()
+        })
+        .collect();
+    wc
+}
+
+fn filtered_generics<'a>(
+    params: impl Iterator<Item = syn::GenericParam> + 'a,
+    valids: &'a HashSet<Ident>,
+) -> impl Iterator<Item = syn::GenericParam> + 'a {
+    params.filter(move |p| match p.maybe_ident() {
+        Some(id) => valids.contains(id),
+        None => false,
+    })
+}
+
+//noinspection RsTypeCheck
+pub(crate) fn generics_clean_up<'a>(
+    original: &Generics,
+    inputs: impl Iterator<Item = &'a FnArg>,
+    output: &ReturnType,
+) -> syn::Generics {
+    let used = references_ident_types(original, inputs, output);
+    let mut result: Generics = original.clone();
+    result.params = filtered_generics(result.params.into_iter(), &used).collect();
+    result.where_clause = result.where_clause.map(|wc| filtered_predicates(wc, &used));
+    result
+}
+
+// If type is not self and doesn't starts with :: return the first ident
+// of its path segment: only if is a simple path.
+// If type is a simple ident just return the this ident. That is useful to
+// find the base type for associate type indication
+fn first_type_path_segment_ident(t: &Type) -> Option<&Ident> {
+    match t {
+        Type::Path(tp) if tp.qself.is_none() && tp.path.leading_colon.is_none() => tp
+            .path
+            .segments
+            .iter()
+            .next()
+            .and_then(|ps| match ps.arguments {
+                syn::PathArguments::None => Some(&ps.ident),
+                _ => None,
+            }),
+        _ => None,
+    }
+}
+
+pub(crate) fn fn_arg_mutability(arg: &FnArg) -> Option<syn::token::Mut> {
+    match arg {
+        FnArg::Typed(syn::PatType { pat, .. }) => match pat.as_ref() {
+            syn::Pat::Ident(syn::PatIdent { mutability, .. }) => *mutability,
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use syn::parse_quote;
+
+    use super::*;
+    use crate::test::{assert_eq, *};
+    use mytest::rstest;
+
+    #[test]
+    fn fn_args_idents_should() {
+        let item_fn = parse_quote! {
+            fn the_functon(first: u32, second: u32) {}
+        };
+
+        let mut args = fn_args_idents(&item_fn);
+
+        assert_eq!("first", args.next().unwrap().to_string());
+        assert_eq!("second", args.next().unwrap().to_string());
+    }
+
+    #[test]
+    fn fn_args_has_ident_should() {
+        let item_fn = parse_quote! {
+            fn the_functon(first: u32, second: u32) {}
+        };
+
+        assert!(fn_args_has_ident(&item_fn, &ident("first")));
+        assert!(!fn_args_has_ident(&item_fn, &ident("third")));
+    }
+
+    #[rstest]
+    #[case::base("fn foo<A, B, C>(a: A) -> B {}", &["A", "B"])]
+    #[case::use_const_in_array("fn foo<A, const B: usize, C>(a: A) -> [u32; B] {}", &["A", "B", "u32"])]
+    #[case::in_type_args("fn foo<A, const B: usize, C>(a: A) -> SomeType<B> {}", &["A", "B"])]
+    #[case::in_type_args("fn foo<A, const B: usize, C>(a: SomeType<A>, b: SomeType<B>) {}", &["A", "B"])]
+    #[case::pointers("fn foo<A, B, C>(a: *const A, b: &B) {}", &["A", "B"])]
+    #[case::lifetime("fn foo<'a, A, B, C>(a: A, b: &'a B) {}", &["a", "A", "B"])]
+    #[case::transitive_lifetime("fn foo<'a, A, B, C>(a: A, b: B) where B: Iterator<Item=A> + 'a {}", &["a", "A", "B"])]
+    #[case::associated("fn foo<'a, A:Copy, C>(b: impl Iterator<Item=A> + 'a) {}", &["a", "A"])]
+    #[case::transitive_in_defs("fn foo<A:Copy, B: Iterator<Item=A>>(b: B) {}", &["A", "B"])]
+    #[case::transitive_in_where("fn foo<A:Copy, B>(b: B) where B: Iterator<Item=A> {}", &["A", "B"])]
+    #[case::transitive_const("fn foo<const A: usize, B, C>(b: B) where B: Some<A> {}", &["A", "B"])]
+    #[case::transitive_lifetime("fn foo<'a, A, B, C>(a: A, b: B) where B: Iterator<Item=A> + 'a {}", &["a", "A", "B"])]
+    #[case::transitive_lifetime(r#"fn foo<'a, 'b, 'c, 'd, A, B, C>
+        (a: A, b: B) 
+        where B: Iterator<Item=A> + 'c, 
+        'c: 'a + 'b {}"#, &["a", "b", "c", "A", "B"])]
+    fn references_ident_types_should(#[case] f: &str, #[case] expected: &[&str]) {
+        let f: ItemFn = f.ast();
+        let used = references_ident_types(&f.sig.generics, f.sig.inputs.iter(), &f.sig.output);
+
+        let expected = to_idents!(expected)
+            .into_iter()
+            .collect::<std::collections::HashSet<_>>();
+
+        assert_eq!(expected, used);
+    }
+
+    #[rstest]
+    #[case::remove_not_in_output(
+        r#"fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>() -> (H, B, String, &str)
+                        where F: ToString,
+                        B: Borrow<u32>
+                        {}"#,
+        r#"fn test<B, H: Iterator<Item=u32>>() -> (H, B, String, &str)
+                        where B: Borrow<u32>
+                {}"#
+    )]
+    #[case::not_remove_used_in_arguments(
+        r#"fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>
+                    (h: H, it: impl Iterator<Item=R>, j: &[B])
+                    where F: ToString,
+                    B: Borrow<u32>
+                {}"#,
+        r#"fn test<R: AsRef<str>, B, H: Iterator<Item=u32>>
+                    (h: H, it: impl Iterator<Item=R>, j: &[B])
+                    where
+                    B: Borrow<u32>
+                {}"#
+    )]
+    #[case::dont_remove_transitive(
+        r#"fn test<A, B, C, D, const F: usize, O>(a: A) where 
+            B: AsRef<C>,
+            A: Iterator<Item=[B; F]>,
+            D: ArsRef<O> {}"#,
+        r#"fn test<A, B, C, const F: usize>(a: A) where 
+            B: AsRef<C>,
+            A: Iterator<Item=[B; F]> {}"#
+    )]
+    #[case::remove_unused_lifetime(
+        "fn test<'a, 'b, 'c, 'd, 'e, 'f, 'g, A>(a: &'a uint32, b: impl AsRef<A> + 'b) where 'b: 'c + 'd, A: Copy + 'e, 'f: 'g {}",
+        "fn test<'a, 'b, 'c, 'd, 'e, A>(a: &'a uint32, b: impl AsRef<A> + 'b) where 'b: 'c + 'd, A: Copy + 'e {}"
+    )]
+    #[case::remove_unused_const(
+        r#"fn test<const A: usize, const B: usize, const C: usize, const D: usize, T, O>
+            (a: [u32; A], b: SomeType<B>, c: T) where 
+            T: Iterator<Item=[i32; C]>,
+            O: AsRef<D> 
+            {}"#,
+        r#"fn test<const A: usize, const B: usize, const C: usize, T>
+            (a: [u32; A], b: SomeType<B>, c: T) where 
+            T: Iterator<Item=[i32; C]>
+            {}"#
+    )]
+    fn generics_cleaner(#[case] code: &str, #[case] expected: &str) {
+        // Should remove all generics parameters that are not present in output
+        let item_fn: ItemFn = code.ast();
+
+        let expected: ItemFn = expected.ast();
+
+        let cleaned = generics_clean_up(
+            &item_fn.sig.generics,
+            item_fn.sig.inputs.iter(),
+            &item_fn.sig.output,
+        );
+
+        assert_eq!(expected.sig.generics, cleaned);
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/fixture/mod.rs b/third_party/rust/rstest/v0_12/crate/tests/fixture/mod.rs
new file mode 100644
index 0000000..9521f1a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/fixture/mod.rs
@@ -0,0 +1,340 @@
+use std::path::Path;
+pub use unindent::Unindent;
+
+use super::resources;
+use mytest::*;
+use rstest_test::{assert_in, assert_not_in, Project, Stringable, TestResults};
+
+fn prj(res: &str) -> Project {
+    let path = Path::new("fixture").join(res);
+    crate::prj().set_code_file(resources(path))
+}
+
+fn run_test(res: &str) -> (std::process::Output, String) {
+    let prj = prj(res);
+    (
+        prj.run_tests().unwrap(),
+        prj.get_name().to_owned().to_string(),
+    )
+}
+
+mod should {
+    use rstest_test::CountMessageOccurrence;
+
+    use super::*;
+
+    #[test]
+    fn use_input_fixtures() {
+        let (output, _) = run_test("simple_injection.rs");
+
+        TestResults::new().ok("success").fail("fail").assert(output);
+    }
+
+    #[test]
+    fn create_a_struct_that_return_the_fixture() {
+        let (output, _) = run_test("fixture_struct.rs");
+
+        TestResults::new()
+            .ok("resolve_new")
+            .ok("resolve_default")
+            .ok("injected_new")
+            .ok("injected_default")
+            .assert(output);
+    }
+
+    #[test]
+    fn be_accessible_from_other_module() {
+        let (output, _) = run_test("from_other_module.rs");
+
+        TestResults::new().ok("struct_access").assert(output);
+    }
+
+    #[test]
+    fn not_show_any_warning() {
+        let (output, _) = run_test("no_warning.rs");
+
+        assert_not_in!(output.stderr.str(), "warning:");
+    }
+
+    #[test]
+    fn rename() {
+        let (output, _) = run_test("rename.rs");
+
+        TestResults::new().ok("test").assert(output);
+    }
+
+    mod accept_and_return {
+        use super::*;
+
+        #[test]
+        fn impl_traits() {
+            let (output, _) = run_test("impl.rs");
+
+            TestResults::new()
+                .ok("base_impl_return")
+                .ok("nested_impl_return")
+                .ok("nested_multiple_impl_return")
+                .ok("base_impl_input")
+                .ok("nested_impl_input")
+                .ok("nested_multiple_impl_input")
+                .assert(output);
+        }
+
+        #[test]
+        fn dyn_traits() {
+            let (output, _) = run_test("dyn.rs");
+
+            TestResults::new()
+                .ok("test_dyn_box")
+                .ok("test_dyn_ref")
+                .ok("test_dyn_box_resolve")
+                .ok("test_dyn_ref_resolve")
+                .assert(output);
+        }
+    }
+
+    #[test]
+    fn resolve_async_fixture() {
+        let prj = prj("async_fixture.rs");
+        prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("default_is_async")
+            .ok("use_async_fixture")
+            .ok("use_async_impl_output")
+            .ok("use_async_nest_fixture_default")
+            .ok("use_async_nest_fixture_injected")
+            .ok("use_async_nest_fixture_with_default")
+            .ok("use_two_args_mix_fixture")
+            .ok("use_two_args_mix_fixture_inject_first")
+            .ok("use_two_args_mix_fixture_inject_both")
+            .assert(output);
+    }
+
+    #[test]
+    fn resolve_fixture_generics_by_fixture_input() {
+        let (output, _) = run_test("resolve.rs");
+
+        TestResults::new()
+            .ok("test_u32")
+            .ok("test_i32")
+            .assert(output);
+    }
+
+    #[test]
+    fn use_defined_return_type_if_any() {
+        let (output, _) = run_test("defined_return_type.rs");
+
+        TestResults::new()
+            .ok("resolve")
+            .ok("resolve_partial")
+            .ok("resolve_attrs")
+            .ok("resolve_partial_attrs")
+            .assert(output);
+    }
+
+    #[test]
+    fn clean_up_default_from_unused_generics() {
+        let (output, _) = run_test("clean_up_default_generics.rs");
+
+        TestResults::new()
+            .ok("resolve")
+            .ok("resolve_partial")
+            .assert(output);
+    }
+
+    #[test]
+    fn apply_partial_fixture() {
+        let (output, _) = run_test("partial.rs");
+
+        TestResults::new()
+            .ok("default")
+            .ok("t_partial_1")
+            .ok("t_partial_2")
+            .ok("t_complete")
+            .assert(output);
+    }
+
+    #[test]
+    fn apply_partial_fixture_from_value_attribute() {
+        let (output, _) = run_test("partial_in_attr.rs");
+
+        TestResults::new()
+            .ok("default")
+            .ok("t_partial_1")
+            .ok("t_partial_2")
+            .ok("t_complete")
+            .assert(output);
+    }
+
+    #[rstest]
+    #[case::compact_form("default.rs")]
+    #[case::attrs_form("default_in_attrs.rs")]
+    fn use_input_values_if_any(#[case] file: &str) {
+        let (output, _) = run_test(file);
+
+        TestResults::new()
+            .ok("test_simple")
+            .ok("test_simple_changed")
+            .ok("test_double")
+            .ok("test_double_changed")
+            .ok("test_mixed")
+            .assert(output);
+    }
+
+    #[test]
+    fn convert_literal_string_for_default_values() {
+        let (output, _) = run_test("default_conversion.rs");
+
+        assert_in!(output.stdout.str(), "Cannot parse 'error' to get MyType");
+
+        TestResults::new()
+            .ok("test_base")
+            .ok("test_byte_array")
+            .ok("test_convert_custom")
+            .fail("test_fail_conversion")
+            .assert(output);
+    }
+
+    #[rstest]
+    #[case("once.rs")]
+    #[case::no_return("once_no_return.rs")]
+    #[case::defined_type("once_defined_type.rs")]
+    fn accept_once_attribute_and_call_fixture_just_once(#[case] fname: &str) {
+        let project = prj(fname).with_nocapture();
+
+        let output = project.run_tests().unwrap();
+
+        // Just to see the errors if fixture doesn't compile
+        assert_in!(output.stderr.str(), "Exec fixture() just once");
+
+        let occurences = output.stderr.str().count("Exec fixture() just once");
+
+        assert_eq!(1, occurences);
+    }
+
+    #[test]
+    fn show_correct_errors() {
+        let prj = prj("errors.rs");
+        let output = prj.run_tests().unwrap();
+        let name = prj.get_name();
+
+        assert_in!(output.stderr.str(), "error[E0433]: ");
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                  --> {}/src/lib.rs:12:33
+                   |
+                12 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#,
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error[E0308]: mismatched types
+                 --> {}/src/lib.rs:8:18
+                  |
+                8 |     let a: u32 = "";
+                "#,
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:16:29
+                   |
+                16 | fn error_fixture_wrong_type(fixture: String) {{
+                   |                             ^^^^^^^",
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Missed argument: 'not_a_fixture' should be a test function argument.
+                  --> {}/src/lib.rs:19:11
+                   |
+                19 | #[fixture(not_a_fixture(24))]
+                   |           ^^^^^^^^^^^^^
+                ",
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:33:23
+                   |
+                33 | #[fixture(f("first"), f("second"))]
+                   |                       ^
+                "#,
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error: Cannot apply #[once] to async fixture.
+                  --> {}/src/lib.rs:38:3
+                   |
+                38 | #[once]
+                   |   ^^^^
+                "#,
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error: Cannot apply #[once] on generic fixture.
+                  --> {}/src/lib.rs:43:3
+                   |
+                43 | #[once]
+                   |   ^^^^
+                "#,
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error: Cannot apply #[once] on generic fixture.
+                  --> {}/src/lib.rs:49:3
+                   |
+                49 | #[once]
+                   |   ^^^^
+                "#,
+                name
+            )
+            .unindent()
+        );
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/integration.rs b/third_party/rust/rstest/v0_12/crate/tests/integration.rs
new file mode 100644
index 0000000..93a2d7ef9
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/integration.rs
@@ -0,0 +1,29 @@
+use rstest_test::{sanitize_name, testname, Project};
+
+/// Rstest integration tests
+mod rstest;
+
+/// Fixture's integration tests
+mod fixture;
+
+use lazy_static::lazy_static;
+
+use std::path::{Path, PathBuf};
+use temp_testdir::TempDir;
+
+lazy_static! {
+    static ref ROOT_DIR: TempDir = TempDir::default().permanent();
+    static ref ROOT_PROJECT: Project = Project::new(ROOT_DIR.as_ref());
+}
+
+pub fn prj() -> Project {
+    let prj_name = sanitize_name(testname());
+
+    let prj = ROOT_PROJECT.subproject(&prj_name);
+    prj.add_local_dependency("rstest");
+    prj
+}
+
+pub fn resources<O: AsRef<Path>>(name: O) -> PathBuf {
+    Path::new("tests").join("resources").join(name)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/async_fixture.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/async_fixture.rs
new file mode 100644
index 0000000..f9fc96c
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/async_fixture.rs
@@ -0,0 +1,73 @@
+use std::io::prelude::*;
+
+use rstest::*;
+
+#[fixture]
+async fn async_u32() -> u32 {
+    42
+}
+
+#[fixture]
+async fn nest_fixture(#[future] async_u32: u32) -> u32 {
+    async_u32.await
+}
+
+#[fixture(fortytwo = async { 42 })]
+async fn nest_fixture_with_default(#[future] fortytwo: u32) -> u32 {
+    fortytwo.await
+}
+
+#[rstest]
+async fn default_is_async() {
+    assert_eq!(42, async_u32::default().await);
+}
+
+#[rstest]
+async fn use_async_nest_fixture_default(#[future] nest_fixture: u32) {
+    assert_eq!(42, nest_fixture.await);
+}
+
+#[rstest(nest_fixture(async { 24 }))]
+async fn use_async_nest_fixture_injected(#[future] nest_fixture: u32) {
+    assert_eq!(24, nest_fixture.await);
+}
+
+#[rstest]
+async fn use_async_nest_fixture_with_default(#[future] nest_fixture_with_default: u32) {
+    assert_eq!(42, nest_fixture_with_default.await);
+}
+
+#[rstest]
+async fn use_async_fixture(#[future] async_u32: u32) {
+    assert_eq!(42, async_u32.await);
+}
+
+#[fixture]
+async fn async_impl_output() -> impl Read {
+    std::io::Cursor::new(vec![1, 2, 3, 4, 5])
+}
+
+#[rstest]
+async fn use_async_impl_output<T: Read>(#[future] async_impl_output: T) {
+    let reader = async_impl_output.await;
+}
+
+#[fixture(four = async { 4 }, two = 2)]
+async fn two_args_mix_fixture(#[future] four: u32, two: u32) -> u32 {
+    four.await * 10 + two
+}
+
+#[rstest]
+async fn use_two_args_mix_fixture(#[future] two_args_mix_fixture: u32) {
+    assert_eq!(42, two_args_mix_fixture.await);
+}
+
+#[rstest(two_args_mix_fixture(async { 5 }))]
+async fn use_two_args_mix_fixture_inject_first(#[future] two_args_mix_fixture: u32) {
+    assert_eq!(52, two_args_mix_fixture.await);
+}
+
+#[rstest(two_args_mix_fixture(async { 3 }, 1))]
+async fn use_two_args_mix_fixture_inject_both(#[future] two_args_mix_fixture: u32) {
+    assert_eq!(31, two_args_mix_fixture.await);
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/clean_up_default_generics.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/clean_up_default_generics.rs
new file mode 100644
index 0000000..11fd276
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/clean_up_default_generics.rs
@@ -0,0 +1,31 @@
+use rstest::*;
+
+#[fixture]
+fn s() -> &'static str {
+    "42"
+}
+
+#[fixture]
+fn fx<S: ToString>(s: S) -> usize {
+    s.to_string().len()
+}
+
+#[fixture]
+fn sum() -> usize {
+    42
+}
+
+#[fixture]
+fn fx_double<S: ToString>(sum: usize, s: S) -> usize {
+    s.to_string().len() + sum
+}
+
+#[test]
+fn resolve() {
+    assert_eq!(2, fx::default())
+}
+
+#[test]
+fn resolve_partial() {
+    assert_eq!(12, fx_double::partial_1(10))
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default.rs
new file mode 100644
index 0000000..1bdd080e
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default.rs
@@ -0,0 +1,46 @@
+use rstest::{fixture, rstest};
+
+#[fixture(value = 42)]
+pub fn simple(value: u32) -> u32 {
+    value
+}
+
+#[fixture(value = 21, mult = 2)]
+pub fn double(value: u32, mult: u32) -> u32 {
+    value * mult
+}
+
+#[fixture]
+pub fn middle() -> u32 {
+    2
+}
+
+#[fixture(value = 21, mult = 4)]
+pub fn mixed(value: u32, middle: u32, mult: u32) -> u32 {
+    value * mult / middle
+}
+
+#[rstest]
+fn test_simple(simple: u32) {
+    assert_eq!(simple, 42)
+}
+
+#[rstest(simple(21))]
+fn test_simple_changed(simple: u32) {
+    assert_eq!(simple, 21)
+}
+
+#[rstest]
+fn test_double(double: u32) {
+    assert_eq!(double, 42)
+}
+
+#[rstest(double(20, 3))]
+fn test_double_changed(double: u32) {
+    assert_eq!(double, 60)
+}
+
+#[rstest]
+fn test_mixed(mixed: u32) {
+    assert_eq!(mixed, 42)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_conversion.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_conversion.rs
new file mode 100644
index 0000000..aee1ecf
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_conversion.rs
@@ -0,0 +1,51 @@
+use rstest::{fixture, rstest};
+use std::net::{Ipv4Addr, SocketAddr};
+
+struct MyType(String);
+struct E;
+impl core::str::FromStr for MyType {
+    type Err = E;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "error" => Err(E),
+            inner => Ok(MyType(inner.to_owned())),
+        }
+    }
+}
+
+#[fixture]
+fn base(#[default("1.2.3.4")] ip: Ipv4Addr, #[default(r#"8080"#)] port: u16) -> SocketAddr {
+    SocketAddr::new(ip.into(), port)
+}
+
+#[fixture]
+fn fail(#[default("error")] t: MyType) -> MyType {
+    t
+}
+
+#[fixture]
+fn valid(#[default("some")] t: MyType) -> MyType {
+    t
+}
+
+#[rstest]
+fn test_base(base: SocketAddr) {
+    assert_eq!(base, "1.2.3.4:8080".parse().unwrap());
+}
+
+#[fixture]
+fn byte_array(#[default(b"1234")] some: &[u8]) -> usize {
+    some.len()
+}
+
+#[rstest]
+fn test_byte_array(byte_array: usize) {
+    assert_eq!(4, byte_array);
+}
+
+#[rstest]
+fn test_convert_custom(valid: MyType) {}
+
+#[rstest]
+fn test_fail_conversion(fail: MyType) {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_in_attrs.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_in_attrs.rs
new file mode 100644
index 0000000..20a10a7f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/default_in_attrs.rs
@@ -0,0 +1,46 @@
+use rstest::{fixture, rstest};
+
+#[fixture]
+pub fn simple(#[default(42)] value: u32) -> u32 {
+    value
+}
+
+#[fixture]
+pub fn double(#[default(20 + 1)] value: u32, #[default(1 + 1)] mult: u32) -> u32 {
+    value * mult
+}
+
+#[fixture]
+pub fn middle() -> u32 {
+    2
+}
+
+#[fixture]
+pub fn mixed(#[default(21)] value: u32, middle: u32, #[default(2 + 2)] mult: u32) -> u32 {
+    value * mult / middle
+}
+
+#[rstest]
+fn test_simple(simple: u32) {
+    assert_eq!(simple, 42)
+}
+
+#[rstest(simple(21))]
+fn test_simple_changed(simple: u32) {
+    assert_eq!(simple, 21)
+}
+
+#[rstest]
+fn test_double(double: u32) {
+    assert_eq!(double, 42)
+}
+
+#[rstest(double(20, 3))]
+fn test_double_changed(double: u32) {
+    assert_eq!(double, 60)
+}
+
+#[rstest]
+fn test_mixed(mixed: u32) {
+    assert_eq!(mixed, 42)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/defined_return_type.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/defined_return_type.rs
new file mode 100644
index 0000000..db0315d7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/defined_return_type.rs
@@ -0,0 +1,43 @@
+use rstest::*;
+
+#[fixture]
+pub fn i() -> u32 {
+    42
+}
+
+#[fixture]
+pub fn j() -> i32 {
+    -42
+}
+
+#[fixture(::default<impl Iterator<Item=(u32, i32)>>::partial_1<impl Iterator<Item=(I,i32)>>)]
+pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
+    std::iter::once((i, j))
+}
+
+#[test]
+fn resolve() {
+    assert_eq!((42, -42), fx::default().next().unwrap())
+}
+
+#[test]
+fn resolve_partial() {
+    assert_eq!((42.0, -42), fx::partial_1(42.0).next().unwrap())
+}
+
+#[fixture]
+#[default(impl Iterator<Item=(u32, i32)>)]
+#[partial_1(impl Iterator<Item=(I,i32)>)]
+pub fn fx_attrs<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
+    std::iter::once((i, j))
+}
+
+#[test]
+fn resolve_attrs() {
+    assert_eq!((42, -42), fx_attrs::default().next().unwrap())
+}
+
+#[test]
+fn resolve_partial_attrs() {
+    assert_eq!((42.0, -42), fx_attrs::partial_1(42.0).next().unwrap())
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/dyn.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/dyn.rs
new file mode 100644
index 0000000..27ea0265
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/dyn.rs
@@ -0,0 +1,41 @@
+use rstest::*;
+
+#[fixture]
+fn dyn_box() -> Box<dyn Iterator<Item=i32>> {
+    Box::new(std::iter::once(42))
+}
+
+#[fixture]
+fn dyn_ref() -> &'static dyn ToString {
+    &42
+}
+
+#[fixture]
+fn dyn_box_resolve(mut dyn_box: Box<dyn Iterator<Item=i32>>) -> i32 {
+    dyn_box.next().unwrap()
+}
+
+#[fixture]
+fn dyn_ref_resolve(dyn_ref: &dyn ToString) -> String {
+    dyn_ref.to_string()
+}
+
+#[rstest]
+fn test_dyn_box(mut dyn_box: Box<dyn Iterator<Item=i32>>) {
+    assert_eq!(42, dyn_box.next().unwrap())
+}
+
+#[rstest]
+fn test_dyn_ref(dyn_ref: &dyn ToString) {
+    assert_eq!("42", dyn_ref.to_string())
+}
+
+#[rstest]
+fn test_dyn_box_resolve(dyn_box_resolve: i32) {
+    assert_eq!(42, dyn_box_resolve)
+}
+
+#[rstest]
+fn test_dyn_ref_resolve(dyn_ref_resolve: String) {
+    assert_eq!("42", dyn_ref_resolve)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/errors.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/errors.rs
new file mode 100644
index 0000000..d185104
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/errors.rs
@@ -0,0 +1,52 @@
+use rstest::*;
+
+#[fixture]
+pub fn fixture() -> u32 { 42 }
+
+#[fixture]
+fn error_inner(fixture: u32) {
+    let a: u32 = "";
+}
+
+#[fixture]
+fn error_cannot_resolve_fixture(no_fixture: u32) {
+}
+
+#[fixture]
+fn error_fixture_wrong_type(fixture: String) {
+}
+
+#[fixture(not_a_fixture(24))]
+fn error_inject_an_invalid_fixture(fixture: String) {
+}
+
+#[fixture]
+fn name() -> &'static str {
+    "name"
+}
+
+#[fixture]
+fn f(name: &str) -> String {
+    name.to_owned()
+}
+
+#[fixture(f("first"), f("second"))]
+fn error_inject_a_fixture_more_than_once(f: String) {
+}
+
+#[fixture]
+#[once]
+async fn error_async_once_fixture() {
+}
+
+#[fixture]
+#[once]
+fn error_generics_once_fixture<T: std::fmt::Debug>() -> T {
+    42
+}
+
+#[fixture]
+#[once]
+fn error_generics_once_fixture() -> impl Iterator<Item: u32> {
+    std::iter::once(42)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/fixture_struct.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/fixture_struct.rs
new file mode 100644
index 0000000..a090d39
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/fixture_struct.rs
@@ -0,0 +1,44 @@
+use rstest::fixture;
+
+trait Mult {
+    fn mult(&self, n: u32) -> u32;
+}
+
+struct M(u32);
+
+impl Mult for M {
+    fn mult(&self, n: u32) -> u32 {
+        n * self.0
+    }
+}
+
+#[fixture]
+fn my_fixture() -> u32 { 42 }
+
+#[fixture]
+fn multiplier() -> M {
+    M(2)
+}
+
+#[fixture]
+fn my_fixture_injected(my_fixture: u32, multiplier: impl Mult) -> u32 { multiplier.mult(my_fixture) }
+
+#[test]
+fn resolve_new() {
+    assert_eq!(42, my_fixture::get());
+}
+
+#[test]
+fn resolve_default() {
+    assert_eq!(42, my_fixture::default());
+}
+
+#[test]
+fn injected_new() {
+    assert_eq!(63, my_fixture_injected::get(21, M(3)));
+}
+
+#[test]
+fn injected_default() {
+    assert_eq!(84, my_fixture_injected::default());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/from_other_module.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/from_other_module.rs
new file mode 100644
index 0000000..5eeffab
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/from_other_module.rs
@@ -0,0 +1,14 @@
+
+mod my_mod {
+    use rstest::{fixture};
+
+    #[fixture]
+    pub fn mod_fixture() -> u32 { 42 }
+}
+
+use my_mod::mod_fixture;
+
+#[test]
+fn struct_access() {
+    assert_eq!(42, mod_fixture::default());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/impl.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/impl.rs
new file mode 100644
index 0000000..0e84827
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/impl.rs
@@ -0,0 +1,57 @@
+use rstest::*;
+
+#[fixture]
+fn fx_base_impl_return() -> impl Iterator<Item=u32> { std::iter::once(42) }
+
+#[fixture]
+fn fx_base_impl_input(mut fx_base_impl_return: impl Iterator<Item=u32>) -> u32 {
+    fx_base_impl_return.next().unwrap()
+}
+
+#[rstest]
+fn base_impl_return(mut fx_base_impl_return: impl Iterator<Item=u32>) {
+    assert_eq!(42, fx_base_impl_return.next().unwrap());
+}
+
+#[rstest]
+fn base_impl_input(mut fx_base_impl_input: u32) {
+    assert_eq!(42, fx_base_impl_input);
+}
+
+#[fixture]
+fn fx_nested_impl_return() -> impl Iterator<Item=impl ToString> { std::iter::once(42) }
+
+#[fixture]
+fn fx_nested_impl_input(mut fx_nested_impl_return: impl Iterator<Item=impl ToString>) -> String {
+    fx_nested_impl_return.next().unwrap().to_string()
+}
+
+#[rstest]
+fn nested_impl_return(mut fx_nested_impl_return: impl Iterator<Item=impl ToString>) {
+    assert_eq!("42", fx_nested_impl_return.next().unwrap().to_string());
+}
+
+#[rstest]
+fn nested_impl_input(mut fx_nested_impl_input: String) {
+    assert_eq!("42", &fx_nested_impl_input);
+}
+
+#[fixture]
+fn fx_nested_multiple_impl_return() -> (impl Iterator<Item=impl ToString>, impl ToString) {
+    (std::iter::once(42), 42i32)
+}
+
+#[fixture]
+fn fx_nested_multiple_impl_input(mut fx_nested_multiple_impl_return: (impl Iterator<Item=impl ToString>, impl ToString)) -> bool {
+    fx_nested_multiple_impl_return.0.next().unwrap().to_string() == fx_nested_multiple_impl_return.1.to_string()
+}
+
+#[rstest]
+fn nested_multiple_impl_return(mut fx_nested_multiple_impl_return: (impl Iterator<Item=impl ToString>, impl ToString)) {
+    assert_eq!(fx_nested_multiple_impl_return.0.next().unwrap().to_string(), fx_nested_multiple_impl_return.1.to_string());
+}
+
+#[rstest]
+fn nested_multiple_impl_input(fx_nested_multiple_impl_input: bool) {
+    assert!(fx_nested_multiple_impl_input);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/no_warning.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/no_warning.rs
new file mode 100644
index 0000000..5fe1f7e0
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/no_warning.rs
@@ -0,0 +1,10 @@
+use rstest::*;
+
+#[fixture]
+fn val() -> i32 { 21 }
+
+#[fixture]
+fn fortytwo(mut val: i32) -> i32 { val *= 2; val }
+
+#[rstest]
+fn the_test(fortytwo: i32) { assert_eq!(fortytwo, 42); }
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once.rs
new file mode 100644
index 0000000..31f637c
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once.rs
@@ -0,0 +1,21 @@
+use rstest::{fixture, rstest};
+
+#[fixture]
+#[once]
+fn once_fixture() -> u32 {
+    eprintln!("Exec fixture() just once");
+    42
+}
+
+#[rstest]
+fn base(once_fixture: &u32) {
+    assert_eq!(&42, once_fixture);
+}
+
+#[rstest]
+#[case(2)]
+#[case(3)]
+#[case(7)]
+fn cases(once_fixture: &u32, #[case] divisor: u32) {
+    assert_eq!(0, *once_fixture % divisor);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_defined_type.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_defined_type.rs
new file mode 100644
index 0000000..1ac439c
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_defined_type.rs
@@ -0,0 +1,33 @@
+use rstest::{fixture, rstest};
+
+#[fixture]
+#[default(u32)]
+#[partial_1(u32)]
+#[once]
+fn once_fixture(#[default(())] a: (), #[default(())] b: ()) -> u32 {
+    eprintln!("Exec fixture() just once");
+    42
+}
+
+#[rstest]
+fn base(once_fixture: &u32) {
+    assert_eq!(&42, once_fixture);
+}
+
+#[rstest]
+fn base_partial(#[with(())] once_fixture: &u32) {
+    assert_eq!(&42, once_fixture);
+}
+
+#[rstest]
+fn base_complete(#[with((), ())] once_fixture: &u32) {
+    assert_eq!(&42, once_fixture);
+}
+
+#[rstest]
+#[case(2)]
+#[case(3)]
+#[case(7)]
+fn cases(once_fixture: &u32, #[case] divisor: u32) {
+    assert_eq!(0, *once_fixture % divisor);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_no_return.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_no_return.rs
new file mode 100644
index 0000000..26674c9196
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/once_no_return.rs
@@ -0,0 +1,20 @@
+use rstest::*;
+
+#[fixture]
+#[once]
+fn once_fixture() {
+    eprintln!("Exec fixture() just once");
+}
+
+#[rstest]
+fn base(_once_fixture: ()) {
+    assert!(true);
+}
+
+#[rstest]
+#[case()]
+#[case()]
+#[case()]
+fn cases(_once_fixture: ()) {
+    assert!(true);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial.rs
new file mode 100644
index 0000000..5afbe02
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial.rs
@@ -0,0 +1,40 @@
+use rstest::*;
+
+#[fixture]
+fn f1() -> u32 { 0 }
+#[fixture]
+fn f2() -> u32 { 0 }
+#[fixture]
+fn f3() -> u32 { 0 }
+
+#[fixture]
+fn fixture(f1: u32, f2: u32, f3: u32) -> u32 { f1 + 10 * f2 + 100 * f3 }
+
+#[fixture(fixture(7))]
+fn partial_1(fixture: u32) -> u32 { fixture }
+
+#[fixture(fixture(2, 4))]
+fn partial_2(fixture: u32) -> u32 { fixture }
+
+#[fixture(fixture(2, 4, 5))]
+fn complete(fixture: u32) -> u32 { fixture }
+
+#[rstest]
+fn default(fixture: u32) {
+    assert_eq!(fixture, 0);
+}
+
+#[rstest]
+fn t_partial_1(partial_1: u32) {
+    assert_eq!(partial_1, 7);
+}
+
+#[rstest]
+fn t_partial_2(partial_2: u32) {
+    assert_eq!(partial_2, 42);
+}
+
+#[rstest]
+fn t_complete(complete: u32) {
+    assert_eq!(complete, 542);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial_in_attr.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial_in_attr.rs
new file mode 100644
index 0000000..3564e8956
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/partial_in_attr.rs
@@ -0,0 +1,54 @@
+use rstest::*;
+
+#[fixture]
+fn f1() -> u32 {
+    0
+}
+#[fixture]
+fn f2() -> u32 {
+    0
+}
+#[fixture]
+fn f3() -> u32 {
+    0
+}
+
+#[fixture]
+fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
+    f1 + 10 * f2 + 100 * f3
+}
+
+#[fixture]
+fn partial_1(#[with(7)] fixture: u32) -> u32 {
+    fixture
+}
+
+#[fixture]
+fn partial_2(#[with(2, 4)] fixture: u32) -> u32 {
+    fixture
+}
+
+#[fixture]
+fn complete(#[with(2, 4, 5)] fixture: u32) -> u32 {
+    fixture
+}
+
+#[rstest]
+fn default(fixture: u32) {
+    assert_eq!(fixture, 0);
+}
+
+#[rstest]
+fn t_partial_1(partial_1: u32) {
+    assert_eq!(partial_1, 7);
+}
+
+#[rstest]
+fn t_partial_2(partial_2: u32) {
+    assert_eq!(partial_2, 42);
+}
+
+#[rstest]
+fn t_complete(complete: u32) {
+    assert_eq!(complete, 542);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/rename.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/rename.rs
new file mode 100644
index 0000000..c3994908
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/rename.rs
@@ -0,0 +1,36 @@
+use rstest::*;
+
+#[fixture]
+fn very_long_and_boring_name(#[default(42)] inject: u32) -> u32 {
+    inject
+}
+
+#[fixture(very_long_and_boring_name as foo)]
+fn compact(foo: u32) -> u32 {
+    foo
+}
+
+#[fixture(very_long_and_boring_name(21) as foo)]
+fn compact_injected(foo: u32) -> u32 {
+    foo
+}
+
+#[fixture]
+fn attribute(#[from(very_long_and_boring_name)] foo: u32) -> u32 {
+    foo
+}
+
+#[fixture]
+fn attribute_injected(
+    #[from(very_long_and_boring_name)]
+    #[with(21)]
+    foo: u32,
+) -> u32 {
+    foo
+}
+
+#[rstest]
+fn test(compact: u32, attribute: u32, compact_injected: u32, attribute_injected: u32) {
+    assert_eq!(compact, attribute);
+    assert_eq!(compact_injected, attribute_injected);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/resolve.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/resolve.rs
new file mode 100644
index 0000000..696ea62
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/resolve.rs
@@ -0,0 +1,42 @@
+use rstest::{rstest, fixture};
+
+pub trait Tr {
+    fn get() -> Self;
+}
+
+impl Tr for i32 {
+    fn get() -> Self {
+        42
+    }
+}
+
+impl Tr for u32 {
+    fn get() -> Self {
+        42
+    }
+}
+
+#[fixture]
+pub fn f<T: Tr>() -> T {
+    T::get()
+}
+
+#[fixture]
+pub fn fu32(f: u32) -> u32 {
+    f
+}
+
+#[fixture]
+pub fn fi32(f: i32) -> i32 {
+    f
+}
+
+#[rstest]
+fn test_u32(fu32: u32) {
+    assert_eq!(fu32, 42)
+}
+
+#[rstest]
+fn test_i32(fi32: i32) {
+    assert_eq!(fi32, 42)
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/simple_injection.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/simple_injection.rs
new file mode 100644
index 0000000..fc83cba
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/fixture/simple_injection.rs
@@ -0,0 +1,17 @@
+use rstest::{rstest, fixture};
+
+#[fixture]
+fn root() -> u32 { 21 }
+
+#[fixture]
+fn injection(root: u32) -> u32 { 2 * root }
+
+#[rstest]
+fn success(injection: u32) {
+    assert_eq!(42, injection);
+}
+
+#[rstest]
+fn fail(injection: u32) {
+    assert_eq!(41, injection);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/args_with_no_cases.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/args_with_no_cases.rs
new file mode 100644
index 0000000..9b857ae5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/args_with_no_cases.rs
@@ -0,0 +1,4 @@
+use rstest::rstest;
+
+#[rstest(one, two, three)]
+fn should_show_error_for_no_case(one: u32, two: u32, three: u32) {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/async.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/async.rs
new file mode 100644
index 0000000..abc7deb2
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/async.rs
@@ -0,0 +1,18 @@
+use rstest::*;
+
+#[rstest]
+#[case::pass(42, async { 42 })]
+#[case::fail(42, async { 41 })]
+#[should_panic]
+#[case::pass_panic(42, async { 41 })]
+#[should_panic]
+#[case::fail_panic(42, async { 42 })]
+async fn my_async_test(#[case] expected: u32, #[case] #[future] value: u32) {
+    assert_eq!(expected, value.await);
+}
+
+#[rstest]
+#[case::pass(42, async { 42 })]
+async fn my_async_test_revert(#[case] expected: u32, #[future] #[case] value: u32) {
+    assert_eq!(expected, value.await);
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_attributes.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_attributes.rs
new file mode 100644
index 0000000..7aa5a0fb
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_attributes.rs
@@ -0,0 +1,24 @@
+use rstest::rstest;
+
+#[rstest(
+    val,
+    case::no_panic(0),
+    #[should_panic]
+    case::panic(2),
+    #[should_panic(expected="expected")]
+    case::panic_with_message(3),
+    case::no_panic_but_fail(1),
+    #[should_panic]
+    case::panic_but_fail(0),
+    #[should_panic(expected="other")]
+    case::panic_with_wrong_message(3),
+)]
+fn attribute_per_case(val: i32) {
+    match val {
+        0 => assert!(true),
+        1 => assert!(false),
+        2 => panic!("No catch"),
+        3 => panic!("expected"),
+        _ => panic!("Not defined"),
+    }
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_with_wrong_args.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_with_wrong_args.rs
new file mode 100644
index 0000000..d6efadf
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/case_with_wrong_args.rs
@@ -0,0 +1,10 @@
+use rstest::rstest;
+
+#[cfg(test)]
+#[rstest(a, b, case(42), case(1, 2), case(43))]
+fn error_less_arguments(a: u32, b: u32) {}
+
+#[cfg(test)]
+#[rstest(a, case(42, 43), case(12), case(24, 34))]
+fn error_too_much_arguments(a: u32) {}
+
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/description.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/description.rs
new file mode 100644
index 0000000..c57ae4b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/description.rs
@@ -0,0 +1,11 @@
+use rstest::rstest;
+
+#[rstest(
+    expected,
+    case::user_test_description(true),
+    case(true),
+    case::user_test_description_fail(false)
+)]
+fn description(expected: bool) {
+    assert!(expected);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/dump_just_one_case.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/dump_just_one_case.rs
new file mode 100644
index 0000000..f8e80d4
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/dump_just_one_case.rs
@@ -0,0 +1,10 @@
+use rstest::*;
+
+#[rstest]
+#[case::first_no_dump("Please don't trace me")]
+#[trace]
+#[case::dump_me("Trace it!")]
+#[case::last_no_dump("Please don't trace me")]
+fn cases(#[case] s: &str) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/inject.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/inject.rs
new file mode 100644
index 0000000..eb2dc75
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/inject.rs
@@ -0,0 +1,21 @@
+use rstest::*;
+use actix_rt;
+use std::future::Future;
+
+#[rstest(expected, value,
+    case::pass(42, 42),
+    #[should_panic]
+    case::panic(41, 42),
+    case::fail(1, 42)
+)]
+#[test]
+fn sync(expected: u32, value: u32) { assert_eq!(expected, value); }
+
+#[rstest(expected, value,
+    case::pass(42, async { 42 }),
+    #[should_panic]
+    case::panic(41, async { 42 }),
+    case::fail(1, async { 42 })
+)]
+#[actix_rt::test]
+async fn fn_async(expected: u32, value: impl Future<Output=u32>) { assert_eq!(expected, value.await); }
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_argument.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_argument.rs
new file mode 100644
index 0000000..e11e2d9
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_argument.rs
@@ -0,0 +1,5 @@
+use rstest::rstest;
+
+#[cfg(test)]
+#[rstest(f, case(42), case(24))]
+fn error_param_not_exist() {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_some_arguments.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_some_arguments.rs
new file mode 100644
index 0000000..7251997
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/missed_some_arguments.rs
@@ -0,0 +1,5 @@
+use rstest::rstest;
+
+#[cfg(test)]
+#[rstest(a,b,c, case(1,2,3), case(3,2,1))]
+fn error_param_not_exist(b: u32) {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/partial.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/partial.rs
new file mode 100644
index 0000000..bb4d863e
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/partial.rs
@@ -0,0 +1,54 @@
+use rstest::*;
+
+#[fixture]
+fn f1() -> u32 {
+    0
+}
+#[fixture]
+fn f2() -> u32 {
+    0
+}
+#[fixture]
+fn f3() -> u32 {
+    0
+}
+
+#[fixture]
+fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
+    f1 + 10 * f2 + 100 * f3
+}
+
+#[rstest(expected, case(0), case(1000))]
+fn default(fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(fixture(7), expected, case(7), case(1000))]
+fn partial_1(fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(expected, case(7), case(1000))]
+fn partial_attr_1(#[with(7)] fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(fixture(2, 4), expected, case(42), case(1000))]
+fn partial_2(fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(expected, case(42), case(1000))]
+fn partial_attr_2(#[with(2, 4)] fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(fixture(2, 4, 5), expected, case(542), case(1000))]
+fn complete(fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
+
+#[rstest(expected, case(542), case(1000))]
+fn complete_attr(#[with(2, 4, 5)] fixture: u32, expected: u32) {
+    assert_eq!(fixture, expected);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/simple.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/simple.rs
new file mode 100644
index 0000000..d699cf7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/simple.rs
@@ -0,0 +1,10 @@
+use rstest::rstest;
+
+#[rstest(
+    expected, input,
+    case(4, "ciao"),
+    case(3, "Foo")
+)]
+fn strlen_test(expected: usize, input: &str) {
+    assert_eq!(expected, input.len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/use_attr.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/use_attr.rs
new file mode 100644
index 0000000..dd45df36
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/cases/use_attr.rs
@@ -0,0 +1,37 @@
+use rstest::rstest;
+
+#[rstest]
+#[case::ciao(4, "ciao")]
+#[should_panic]
+#[case::panic(42, "Foo")]
+#[case::foo(3, "Foo")]
+fn all(#[case] expected: usize, #[case] input: &str) {
+    assert_eq!(expected, input.len());
+}
+
+#[rstest(expected, input)]
+#[case::ciao(4, "ciao")]
+#[case::foo(3, "Foo")]
+#[should_panic]
+#[case::panic(42, "Foo")]
+fn just_cases(expected: usize, input: &str) {
+    assert_eq!(expected, input.len());
+}
+
+#[rstest(
+    case::ciao(4, "ciao"),
+    case::foo(3, "Foo"),
+    #[should_panic]
+    case::panic(42, "Foo"),
+)]
+fn just_args(#[case] expected: usize, #[case] input: &str) {
+    assert_eq!(expected, input.len());
+}
+
+#[rstest]
+#[case(0, "ciao")]
+#[case(0, "Foo")]
+#[should_panic]
+fn all_panic(#[case] expected: usize, #[case] input: &str) {
+    assert_eq!(expected, input.len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/convert_string_literal.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/convert_string_literal.rs
new file mode 100644
index 0000000..3e7989f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/convert_string_literal.rs
@@ -0,0 +1,75 @@
+use rstest::*;
+use std::net::SocketAddr;
+
+#[rstest]
+#[case(true, "1.2.3.4:42")]
+#[case(true, r#"4.3.2.1:24"#)]
+#[case(false, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443")]
+#[case(false, r#"[2aa1:db8:85a3:8af:1319:8a2e:375:4873]:344"#)]
+#[case(false, "this.is.not.a.socket.address")]
+#[case(false, r#"this.is.not.a.socket.address"#)]
+fn cases(#[case] expected: bool, #[case] addr: SocketAddr) {
+    assert_eq!(expected, addr.is_ipv4());
+}
+
+#[rstest]
+fn values(
+    #[values(
+        "1.2.3.4:42",
+        r#"4.3.2.1:24"#,
+        "this.is.not.a.socket.address",
+        r#"this.is.not.a.socket.address"#
+    )]
+    addr: SocketAddr,
+) {
+    assert!(addr.is_ipv4())
+}
+
+#[rstest]
+#[case(b"12345")]
+fn not_convert_byte_array(#[case] cases: &[u8], #[values(b"abc")] values: &[u8]) {
+    assert_eq!(5, cases.len());
+    assert_eq!(3, values.len());
+}
+
+trait MyTrait {
+    fn my_trait(&self) -> u32 {
+        42
+    }
+}
+
+impl MyTrait for &str {}
+
+#[rstest]
+#[case("impl", "nothing")]
+fn not_convert_impl(#[case] that_impl: impl MyTrait, #[case] s: &str) {
+    assert_eq!(42, that_impl.my_trait());
+    assert_eq!(42, s.my_trait());
+}
+
+#[rstest]
+#[case("1.2.3.4", "1.2.3.4:42")]
+#[case("1.2.3.4".to_owned(), "1.2.3.4:42")]
+fn not_convert_generics<S: AsRef<str>>(#[case] ip: S, #[case] addr: SocketAddr) {
+    assert_eq!(addr.ip().to_string(), ip.as_ref());
+}
+
+struct MyType(String);
+struct E;
+impl core::str::FromStr for MyType {
+    type Err = E;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "error" => Err(E),
+            inner => Ok(MyType(inner.to_owned())),
+        }
+    }
+}
+
+#[rstest]
+#[case("hello", "hello")]
+#[case("doesn't mater", "error")]
+fn convert_without_debug(#[case] expected: &str, #[case] converted: MyType) {
+    assert_eq!(expected, converted.0);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug.rs
new file mode 100644
index 0000000..7071cf4a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug.rs
@@ -0,0 +1,41 @@
+use rstest::*;
+
+#[derive(Debug)]
+struct A {}
+
+#[fixture]
+fn fu32() -> u32 {
+    42
+}
+#[fixture]
+fn fstring() -> String {
+    "A String".to_string()
+}
+#[fixture]
+fn ftuple() -> (A, String, i32) {
+    (A {}, "A String".to_string(), -12)
+}
+
+#[rstest]
+#[trace]
+fn single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
+    assert!(false);
+}
+
+#[rstest]
+#[case(42, "str", ("ss", -12))]
+#[case(24, "trs", ("tt", -24))]
+#[trace]
+fn cases_fail(#[case] u: u32, #[case] s: &str, #[case] t: (&str, i32)) {
+    assert!(false);
+}
+
+#[rstest]
+#[trace]
+fn matrix_fail(
+    #[values(1, 3)] u: u32,
+    #[values("rst", "srt")] s: &str,
+    #[values(("SS", -12), ("TT", -24))] t: (&str, i32),
+) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug_compact.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug_compact.rs
new file mode 100644
index 0000000..1192629e
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_debug_compact.rs
@@ -0,0 +1,35 @@
+use rstest::*;
+
+#[derive(Debug)]
+struct A {}
+
+#[fixture]
+fn fu32() -> u32 { 42 }
+#[fixture]
+fn fstring() -> String { "A String".to_string() }
+#[fixture]
+fn ftuple() -> (A, String, i32) { (A{}, "A String".to_string(), -12) }
+
+#[rstest(::trace)]
+fn single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
+    assert!(false);
+}
+
+#[rstest(u, s, t,
+    case(42, "str", ("ss", -12)),
+    case(24, "trs", ("tt", -24))
+    ::trace
+)]
+fn cases_fail(u: u32, s: &str, t: (&str, i32)) {
+    assert!(false);
+}
+
+#[rstest(
+    u => [1, 2],
+    s => ["rst", "srt"],
+    t => [("SS", -12), ("TT", -24)]
+    ::trace
+)]
+fn matrix_fail(u: u32, s: &str, t: (&str, i32)) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs.rs
new file mode 100644
index 0000000..4775cd2
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs.rs
@@ -0,0 +1,51 @@
+use rstest::*;
+
+struct A;
+struct B;
+#[derive(Debug)]
+struct D;
+
+#[fixture]
+fn fu32() -> u32 {
+    42
+}
+#[fixture]
+fn fb() -> B {
+    B {}
+}
+#[fixture]
+fn fd() -> D {
+    D {}
+}
+#[fixture]
+fn fa() -> A {
+    A {}
+}
+
+#[rstest]
+#[trace]
+fn simple(fu32: u32, #[notrace] fa: A, #[notrace] fb: B, fd: D) {
+    assert!(false);
+}
+
+#[rstest]
+#[trace]
+#[case(A{}, B{}, D{})]
+fn cases(fu32: u32, #[case] #[notrace] a: A, #[case] #[notrace] b: B, #[case] d: D) {
+    assert!(false);
+}
+
+#[rstest]
+#[trace]
+fn matrix(
+    fu32: u32,
+    #[notrace]
+    #[values(A{})]
+    a: A,
+    #[notrace]
+    #[values(B{})]
+    b: B,
+    #[values(D{}) ] dd: D,
+) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs_compact.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs_compact.rs
new file mode 100644
index 0000000..d8de32f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_exclude_some_inputs_compact.rs
@@ -0,0 +1,41 @@
+use rstest::*;
+
+struct A;
+struct B;
+#[derive(Debug)]
+struct D;
+
+#[fixture]
+fn fu32() -> u32 { 42 }
+#[fixture]
+fn fb() -> B { B {} }
+#[fixture]
+fn fd() -> D { D {} }
+#[fixture]
+fn fa() -> A { A {} }
+
+
+#[rstest(
+::trace::notrace(fa,fb))
+]
+fn simple(fu32: u32, fa: A, fb: B, fd: D) {
+    assert!(false);
+}
+
+#[rstest(a,b,d,
+    case(A{}, B{}, D{})
+    ::trace::notrace(a,b))
+]
+fn cases(fu32: u32, a: A, b: B, d: D) {
+    assert!(false);
+}
+
+#[rstest(
+    a => [A{}],
+    b => [B{}],
+    dd => [D{}],
+    ::trace::notrace(a,b))
+]
+fn matrix(fu32: u32, a: A, b: B, dd: D) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug.rs
new file mode 100644
index 0000000..10a97db6a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug.rs
@@ -0,0 +1,21 @@
+struct S;
+#[rustfmt::skip] mod _skip_format {
+use rstest::*; use super::*;
+
+#[fixture]
+fn fixture() -> S { S {} }
+
+#[rstest]
+#[trace]
+fn single(fixture: S) {}
+
+#[rstest(s)]
+#[trace]
+#[case(S{})]
+fn cases(s: S) {}
+
+#[rstest(
+    s => [S{}])]
+#[trace]
+fn matrix(s: S) {}
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug_compact.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug_compact.rs
new file mode 100644
index 0000000..fc640cb4
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/dump_not_debug_compact.rs
@@ -0,0 +1,21 @@
+struct S;
+#[rustfmt::skip] mod _skip_format {
+use rstest::*; use super::*;
+
+#[fixture]
+fn fixture() -> S { S {} }
+
+#[rstest(
+    ::trace)]
+fn single(fixture: S) {}
+
+#[rstest(s,
+    case(S{})
+    ::trace)]
+fn cases(s: S) {}
+
+#[rstest(
+    s => [S{}]
+    ::trace)]
+fn matrix(s: S) {}
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/errors.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/errors.rs
new file mode 100644
index 0000000..565e912
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/errors.rs
@@ -0,0 +1,94 @@
+use rstest::*;
+#[fixture]
+pub fn fixture() -> u32 {
+    42
+}
+
+#[rstest(f, case(42))]
+fn error_inner(f: i32) {
+    let a: u32 = "";
+}
+
+#[rstest(f, case(42))]
+fn error_cannot_resolve_fixture(no_fixture: u32, f: u32) {}
+
+#[rstest(f, case(42))]
+fn error_fixture_wrong_type(fixture: String, f: u32) {}
+
+#[rstest(f, case(42))]
+fn error_case_wrong_type(f: &str) {}
+
+#[rstest(condition,
+    case(vec![1,2,3].contains(2)))
+]
+fn error_in_arbitrary_rust_code_cases(condition: bool) {
+    assert!(condition)
+}
+
+#[rstest(f, case(42), not_a_fixture(24))]
+fn error_inject_an_invalid_fixture(f: u32) {}
+
+#[fixture]
+fn n() -> u32 {
+    24
+}
+
+#[fixture]
+fn f(n: u32) -> u32 {
+    2 * n
+}
+
+#[rstest(f, f(42), case(12))]
+fn error_inject_a_fixture_that_is_already_a_case(f: u32) {}
+
+#[rstest(f(42), f, case(12))]
+fn error_define_case_that_is_already_an_injected_fixture(f: u32) {}
+
+#[rstest(v, f(42), f(42), case(12))]
+fn error_inject_a_fixture_more_than_once(v: u32, f: u32) {}
+
+#[rstest(f => [42])]
+fn error_matrix_wrong_type(f: &str) {}
+
+#[rstest(condition => [vec![1,2,3].contains(2)] )]
+fn error_arbitrary_rust_code_matrix(condition: bool) {
+    assert!(condition)
+}
+
+#[rstest(empty => [])]
+fn error_empty_list(empty: &str) {}
+
+#[rstest(not_exist_1 => [42],
+         not_exist_2 => [42])]
+fn error_no_match_args() {}
+
+#[rstest(f => [41, 42], f(42))]
+fn error_inject_a_fixture_that_is_already_a_value_list(f: u32) {}
+
+#[rstest(f(42), f => [41, 42])]
+fn error_define_a_value_list_that_is_already_an_injected_fixture(f: u32) {}
+
+#[rstest(a, case(42), a => [42])]
+fn error_define_a_value_list_that_is_already_a_case_arg(a: u32) {}
+
+#[rstest(a => [42], a, case(42))]
+fn error_define_a_case_arg_that_is_already_a_value_list(a: u32) {}
+
+#[rstest(a => [42, 24], a => [24, 42])]
+fn error_define_a_value_list_that_is_already_a_value_list(f: u32) {}
+
+#[rstest(a, a, case(42))]
+fn error_define_a_case_arg_that_is_already_a_case_arg(a: u32) {}
+
+struct S;
+#[rstest]
+#[case("donald duck")]
+fn error_convert_to_type_that_not_implement_from_str(#[case] s: S) {}
+
+#[rstest]
+#[case(async { "hello" } )]
+async fn error_future_on_impl_type(#[case] #[future] s: impl AsRef<str>) {}
+
+#[rstest]
+#[case(async { 42 } )]
+async fn error_future_on_impl_type(#[case] #[future] #[future] a: i32) {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/generic.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/generic.rs
new file mode 100644
index 0000000..4da8de7a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/generic.rs
@@ -0,0 +1,18 @@
+use rstest::*;
+
+#[fixture]
+fn fixture() -> String { "str".to_owned() }
+
+#[rstest]
+fn simple<S: AsRef<str>>(fixture: S) {
+    assert_eq!(3, fixture.as_ref().len());
+}
+
+#[rstest(
+    expected, input,
+    case(4, String::from("ciao")),
+    case(3, "Foo")
+)]
+fn strlen_test<S: AsRef<str>>(expected: usize, input: S) {
+    assert_eq!(expected, input.as_ref().len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/happy_path.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/happy_path.rs
new file mode 100644
index 0000000..1a4172f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/happy_path.rs
@@ -0,0 +1,35 @@
+use rstest::*;
+
+#[fixture]
+fn inject() -> u32 {
+    0
+}
+
+#[fixture]
+fn ex() -> u32 {
+    42
+}
+
+#[fixture]
+fn fix(inject: u32, ex: u32) -> bool {
+    (inject * 2) == ex
+}
+
+#[rstest(
+    fix(21),
+    a,
+    case(21, 2),
+    expected => [4, 2*3-2],
+)]
+#[case::second(14, 3)]
+fn happy(
+    fix: bool,
+    a: u32,
+    #[case] b: u32,
+    expected: usize,
+    #[values("ciao", "buzz")] input: &str,
+) {
+    assert!(fix);
+    assert_eq!(a * b, 42);
+    assert_eq!(expected, input.len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/ignore_args.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/ignore_args.rs
new file mode 100644
index 0000000..fd5a8aa5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/ignore_args.rs
@@ -0,0 +1,6 @@
+use rstest::*;
+
+#[rstest]
+#[case(42, 2)]
+#[case(43, 3)]
+fn test(#[case] _ignore1: u32, #[case] _ignore2: u32, #[values(1, 2, 3, 4)] _ignore3: u32) {}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/impl_param.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/impl_param.rs
new file mode 100644
index 0000000..f366401
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/impl_param.rs
@@ -0,0 +1,18 @@
+use rstest::*;
+
+#[fixture]
+fn fixture() -> String { "str".to_owned() }
+
+#[rstest]
+fn simple(fixture: impl AsRef<str>) {
+    assert_eq!(3, fixture.as_ref().len());
+}
+
+#[rstest(
+    expected, input,
+    case(4, String::from("ciao")),
+    case(3, "Foo")
+)]
+fn strlen_test(expected: usize, input: impl AsRef<str>) {
+    assert_eq!(expected, input.as_ref().len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/async.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/async.rs
new file mode 100644
index 0000000..d36f01e9
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/async.rs
@@ -0,0 +1,12 @@
+use rstest::*;
+
+#[rstest]
+async fn my_async_test(
+    #[future] 
+    #[values(async { 1 }, async { 2 })] 
+    first: u32, 
+    #[values(42, 21)] 
+    second: u32
+) {
+    assert_eq!(42, first.await * second);
+}
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/inject.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/inject.rs
new file mode 100644
index 0000000..6547f09b0
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/inject.rs
@@ -0,0 +1,17 @@
+use rstest::*;
+use actix_rt;
+use std::future::Future;
+
+#[rstest(
+    first => [1, 2], 
+    second => [2, 1],
+)]
+#[test]
+fn sync(first: u32, second: u32) { assert_eq!(2, first * second); }
+
+#[rstest(
+    first => [async { 1 }, async { 2 }], 
+    second => [2, 1],
+)]
+#[actix_rt::test]
+async fn fn_async(first: impl Future<Output=u32>, second: u32) { assert_eq!(2, first.await * second); }
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/partial.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/partial.rs
new file mode 100644
index 0000000..40317b5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/partial.rs
@@ -0,0 +1,54 @@
+use rstest::*;
+
+#[fixture]
+fn f1() -> u32 {
+    0
+}
+#[fixture]
+fn f2() -> u32 {
+    0
+}
+#[fixture]
+fn f3() -> u32 {
+    0
+}
+
+#[fixture]
+fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
+    f1 + f2 + 2 * f3
+}
+
+#[rstest(a => [0, 1], b => [0, 2])]
+fn default(fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2], fixture(1))]
+fn partial_1(fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2])]
+fn partial_attr_1(#[with(1)] fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2], fixture(0, 2))]
+fn partial_2(fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2])]
+fn partial_attr_2(#[with(0, 2)] fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2], fixture(0, 0, 1))]
+fn complete(fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
+
+#[rstest(a => [0, 1], b => [0, 2])]
+fn complete_attr(#[with(0, 0, 1)] fixture: u32, a: u32, b: u32) {
+    assert_eq!(fixture, a * b);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/simple.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/simple.rs
new file mode 100644
index 0000000..d5ef2d08
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/simple.rs
@@ -0,0 +1,9 @@
+use rstest::rstest;
+
+#[rstest(
+    expected => [4, 2*3-2],
+    input => ["ciao", "buzz"],
+)]
+fn strlen_test(expected: usize, input: &str) {
+    assert_eq!(expected, input.len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/use_attr.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/use_attr.rs
new file mode 100644
index 0000000..7b6e6f7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/matrix/use_attr.rs
@@ -0,0 +1,20 @@
+use rstest::rstest;
+
+#[rstest]
+fn both(#[values(4, 2*3-2)] expected: usize, #[values("ciao", "buzz")] input: &str) {
+    assert_eq!(expected, input.len());
+}
+
+#[rstest(
+    input => ["ciao", "buzz"]
+)]
+fn first(#[values(4, 2*3-2)] expected: usize, input: &str) {
+    assert_eq!(expected, input.len());
+}
+
+#[rstest(
+    expected => [4, 2*3-2]
+)]
+fn second(expected: usize, #[values("ciao", "buzz")] input: &str) {
+    assert_eq!(expected, input.len());
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/mut.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/mut.rs
new file mode 100644
index 0000000..1f45844
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/mut.rs
@@ -0,0 +1,29 @@
+use rstest::*;
+
+#[fixture]
+pub fn fixture() -> u32 { 42 }
+
+#[rstest]
+fn should_success(mut fixture: u32) {
+    fixture += 1;
+    assert_eq!(fixture, 43);
+}
+
+#[rstest]
+fn should_fail(mut fixture: u32) {
+    fixture += 1;
+    assert_ne!(fixture, 43);
+}
+
+#[rstest(
+    expected, val,
+    case(45, 1),
+    case(46, 2),
+    case(47, 2)
+)]
+fn add_test(mut fixture: u32, expected: u32, mut val: u32) {
+    fixture += 1;
+    val += fixture + 1;
+
+    assert_eq!(expected, val);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/panic.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/panic.rs
new file mode 100644
index 0000000..a86c97a
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/panic.rs
@@ -0,0 +1,27 @@
+use rstest::*;
+
+#[fixture]
+pub fn fixture() -> u32 { 42 }
+
+#[rstest]
+#[should_panic]
+fn should_success(fixture: u32) {
+    assert_ne!(fixture, 42);
+}
+
+#[rstest]
+#[should_panic]
+fn should_fail(fixture: u32) {
+    assert_eq!(fixture, 42);
+}
+
+#[rstest(
+    expected, input,
+    case(4, 5),
+    case(3, 2),
+    case(3, 3)
+)]
+#[should_panic]
+fn fail(expected: i32, input: i32) {
+    assert_eq!(expected, input);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/reject_no_item_function.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/reject_no_item_function.rs
new file mode 100644
index 0000000..d120bdc
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/reject_no_item_function.rs
@@ -0,0 +1,11 @@
+use rstest::rstest;
+
+#[rstest]
+struct Foo;
+
+#[rstest]
+impl Foo {}
+
+#[rstest]
+mod mod_baz {}
+
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/remove_underscore.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/remove_underscore.rs
new file mode 100644
index 0000000..50930a5
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/remove_underscore.rs
@@ -0,0 +1,9 @@
+use rstest::*;
+
+#[fixture]
+fn can_be_ignored() {}
+
+#[rstest]
+fn ignore_input(_can_be_ignored: ()) {
+    assert!(true);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/rename.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/rename.rs
new file mode 100644
index 0000000..ebd442d7
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/rename.rs
@@ -0,0 +1,30 @@
+use rstest::*;
+
+#[fixture]
+fn very_long_and_boring_name(#[default(42)] inject: u32) -> u32 {
+    inject
+}
+
+#[rstest(very_long_and_boring_name as foo)]
+fn compact(foo: u32) {
+    assert!(42 == foo);
+}
+
+#[rstest(very_long_and_boring_name(21) as foo)]
+fn compact_injected(foo: u32) {
+    assert!(21 == foo);
+}
+
+#[rstest]
+fn attribute(#[from(very_long_and_boring_name)] foo: u32) {
+    assert!(42 == foo);
+}
+
+#[rstest]
+fn attribute_injected(
+    #[from(very_long_and_boring_name)]
+    #[with(21)]
+    foo: u32,
+) {
+    assert!(21 == foo);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/return_result.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/return_result.rs
new file mode 100644
index 0000000..bcfb6f6b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/return_result.rs
@@ -0,0 +1,19 @@
+use rstest::rstest;
+
+#[rstest]
+fn should_success() -> Result<(), &'static str> {
+    Ok(())
+}
+
+#[rstest]
+fn should_fail() -> Result<(), &'static str> {
+    Err("Return Error")
+}
+
+#[rstest(ret,
+    case::should_success(Ok(())),
+    case::should_fail(Err("Return Error"))
+)]
+fn return_type(ret: Result<(), &'static str>) -> Result<(), &'static str> {
+    ret
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/async.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/async.rs
new file mode 100644
index 0000000..2a4f6a6
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/async.rs
@@ -0,0 +1,26 @@
+use rstest::*;
+
+#[fixture]
+async fn fixture() -> u32 { 42 }
+
+#[rstest]
+async fn should_pass(#[future] fixture: u32) {
+    assert_eq!(fixture.await, 42);
+}
+
+#[rstest]
+async fn should_fail(#[future] fixture: u32) {
+    assert_ne!(fixture.await, 42);
+}
+
+#[rstest]
+#[should_panic]
+async fn should_panic_pass(#[future] fixture: u32) {
+    panic!(format!("My panic -> fixture = {}", fixture.await));
+}
+
+#[rstest]
+#[should_panic]
+async fn should_panic_fail(#[future] fixture: u32) {
+    assert_eq!(fixture.await, 42);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/dump_debug.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/dump_debug.rs
new file mode 100644
index 0000000..a287932f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/dump_debug.rs
@@ -0,0 +1,16 @@
+use rstest::*;
+
+#[derive(Debug)]
+struct A {}
+
+#[fixture]
+fn fu32() -> u32 { 42 }
+#[fixture]
+fn fstring() -> String { "A String".to_string() }
+#[fixture]
+fn ftuple() -> (A, String, i32) { (A{}, "A String".to_string(), -12) }
+
+#[rstest(::trace)]
+fn should_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
+    assert!(false);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/inject.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/inject.rs
new file mode 100644
index 0000000..c835097b
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/inject.rs
@@ -0,0 +1,41 @@
+use rstest::*;
+use actix_rt;
+
+#[fixture]
+fn a() -> u32 {
+    42
+}
+
+#[rstest]
+#[test]
+fn sync_case(a: u32) {}
+
+#[rstest]
+#[test]
+#[should_panic]
+fn sync_case_panic(a: u32) { panic!("panic") }
+
+#[rstest]
+#[test]
+fn sync_case_fail(a: u32) { assert_eq!(2, a); }
+
+#[rstest]
+#[test]
+fn sync_case_panic_fail(a: u32) { panic!("panic") }
+
+#[rstest]
+#[actix_rt::test]
+async fn async_case(a: u32) {}
+
+#[rstest]
+#[actix_rt::test]
+async fn async_case_fail(a: u32) { assert_eq!(2, a); }
+
+#[rstest]
+#[actix_rt::test]
+#[should_panic]
+async fn async_case_panic(a: u32) { panic!("panic") }
+
+#[rstest]
+#[actix_rt::test]
+async fn async_case_panic_fail(a: u32) { panic!("panic") }
\ No newline at end of file
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/partial.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/partial.rs
new file mode 100644
index 0000000..e653ab2
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/partial.rs
@@ -0,0 +1,54 @@
+use rstest::*;
+
+#[fixture]
+fn f1() -> u32 {
+    0
+}
+#[fixture]
+fn f2() -> u32 {
+    0
+}
+#[fixture]
+fn f3() -> u32 {
+    0
+}
+
+#[fixture]
+fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
+    f1 + 10 * f2 + 100 * f3
+}
+
+#[rstest]
+fn default(fixture: u32) {
+    assert_eq!(fixture, 0);
+}
+
+#[rstest(fixture(7))]
+fn partial_1(fixture: u32) {
+    assert_eq!(fixture, 7);
+}
+
+#[rstest]
+fn partial_attr_1(#[with(7)] fixture: u32) {
+    assert_eq!(fixture, 7);
+}
+
+#[rstest(fixture(2, 4))]
+fn partial_2(fixture: u32) {
+    assert_eq!(fixture, 42);
+}
+
+#[rstest]
+fn partial_attr_2(#[with(2, 4)] fixture: u32) {
+    assert_eq!(fixture, 42);
+}
+
+#[rstest(fixture(2, 4, 5))]
+fn complete(fixture: u32) {
+    assert_eq!(fixture, 542);
+}
+
+#[rstest]
+fn complete_attr(#[with(2, 4, 5)] fixture: u32) {
+    assert_eq!(fixture, 542);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/resolve.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/resolve.rs
new file mode 100644
index 0000000..607caa3
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/resolve.rs
@@ -0,0 +1,32 @@
+use rstest::*;
+
+pub trait Tr {
+    fn get() -> Self;
+}
+
+impl Tr for i32 {
+    fn get() -> Self {
+        42
+    }
+}
+
+impl Tr for u32 {
+    fn get() -> Self {
+        42
+    }
+}
+
+#[fixture]
+pub fn fgen<T: Tr>() -> T {
+    T::get()
+}
+
+#[rstest]
+fn generics_u32(fgen: u32) {
+    assert_eq!(fgen, 42u32);
+}
+
+#[rstest]
+fn generics_i32(fgen: i32) {
+    assert_eq!(fgen, 42i32);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/simple.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/simple.rs
new file mode 100644
index 0000000..b99b5f0
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/single/simple.rs
@@ -0,0 +1,14 @@
+use rstest::*;
+
+#[fixture]
+pub fn fixture() -> u32 { 42 }
+
+#[rstest]
+fn should_success(fixture: u32) {
+    assert_eq!(fixture, 42);
+}
+
+#[rstest]
+fn should_fail(fixture: u32) {
+    assert_ne!(fixture, 42);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs
new file mode 100644
index 0000000..97a6b0f
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs
@@ -0,0 +1,25 @@
+use rstest::*;
+
+#[fixture]
+fn f() -> String {
+    "f".to_owned()
+}
+
+fn append(s: &mut String, a: &str) -> String {
+    s.push_str("-");
+    s.push_str(a);
+    s.clone()
+}
+
+#[rstest]
+#[case(append(&mut f, "a"), "f-a", "f-a-b")]
+fn use_mutate_fixture(
+    mut f: String,
+    #[case] a: String,
+    #[values(append(&mut f, "b"))] b: String,
+    #[case] expected_a: &str,
+    #[case] expected_b: &str,
+) {
+    assert_eq!(expected_a, a);
+    assert_eq!(expected_b, b);
+}
diff --git a/third_party/rust/rstest/v0_12/crate/tests/rstest/mod.rs b/third_party/rust/rstest/v0_12/crate/tests/rstest/mod.rs
new file mode 100644
index 0000000..efd0291
--- /dev/null
+++ b/third_party/rust/rstest/v0_12/crate/tests/rstest/mod.rs
@@ -0,0 +1,1326 @@
+use std::path::Path;
+
+use super::resources;
+
+use mytest::*;
+use rstest_test::*;
+use unindent::Unindent;
+
+fn prj(res: impl AsRef<Path>) -> Project {
+    let path = Path::new("rstest").join(res.as_ref());
+    crate::prj().set_code_file(resources(path))
+}
+
+fn run_test(res: impl AsRef<Path>) -> (std::process::Output, String) {
+    let prj = prj(res);
+    (
+        prj.run_tests().unwrap(),
+        prj.get_name().to_owned().to_string(),
+    )
+}
+
+#[test]
+fn mutable_input() {
+    let (output, _) = run_test("mut.rs");
+
+    TestResults::new()
+        .ok("should_success")
+        .fail("should_fail")
+        .ok("add_test::case_1")
+        .ok("add_test::case_2")
+        .fail("add_test::case_3")
+        .assert(output);
+}
+
+#[test]
+fn test_with_return_type() {
+    let (output, _) = run_test("return_result.rs");
+
+    TestResults::new()
+        .ok("should_success")
+        .fail("should_fail")
+        .ok("return_type::case_1_should_success")
+        .fail("return_type::case_2_should_fail")
+        .assert(output);
+}
+
+#[test]
+fn should_panic() {
+    let (output, _) = run_test("panic.rs");
+
+    TestResults::new()
+        .ok("should_success")
+        .fail("should_fail")
+        .ok("fail::case_1")
+        .ok("fail::case_2")
+        .fail("fail::case_3")
+        .assert(output);
+}
+
+#[test]
+fn should_not_show_a_warning_for_should_panic_attribute() {
+    let (output, _) = run_test("panic.rs");
+
+    assert!(!output.stderr.str().contains("unused attribute"));
+}
+
+#[test]
+fn should_map_fixture_by_remove_first_underscore_if_any() {
+    let (output, _) = run_test("remove_underscore.rs");
+
+    TestResults::new().ok("ignore_input").assert(output);
+}
+
+#[test]
+fn generic_input() {
+    let (output, _) = run_test("generic.rs");
+
+    TestResults::new()
+        .ok("simple")
+        .ok("strlen_test::case_1")
+        .ok("strlen_test::case_2")
+        .assert(output);
+}
+
+#[test]
+fn impl_input() {
+    let (output, _) = run_test("impl_param.rs");
+
+    TestResults::new()
+        .ok("simple")
+        .ok("strlen_test::case_1")
+        .ok("strlen_test::case_2")
+        .assert(output);
+}
+
+#[test]
+fn use_mutable_fixture_in_parametric_argumnts() {
+    let (output, _) = run_test("use_mutable_fixture_in_parametric_argumnts.rs");
+
+    TestResults::new()
+        .ok("use_mutate_fixture::case_1::b_1")
+        .assert(output);
+}
+
+#[test]
+fn should_reject_no_item_function() {
+    let (output, name) = run_test("reject_no_item_function.rs");
+
+    assert_in!(
+        output.stderr.str(),
+        format!(
+            "
+        error: expected `fn`
+         --> {}/src/lib.rs:4:1
+          |
+        4 | struct Foo;
+          | ^^^^^^
+        ",
+            name
+        )
+        .unindent()
+    );
+
+    assert_in!(
+        output.stderr.str(),
+        format!(
+            "
+        error: expected `fn`
+         --> {}/src/lib.rs:7:1
+          |
+        7 | impl Foo {{}}
+          | ^^^^
+        ",
+            name
+        )
+        .unindent()
+    );
+
+    assert_in!(
+        output.stderr.str(),
+        format!(
+            "
+        error: expected `fn`
+          --> {}/src/lib.rs:10:1
+           |
+        10 | mod mod_baz {{}}
+           | ^^^
+        ",
+            name
+        )
+        .unindent()
+    );
+}
+
+mod dump_input_values {
+    use super::*;
+
+    #[rstest]
+    #[case::compact_syntax("dump_debug_compact.rs")]
+    #[case::attr_syntax("dump_debug.rs")]
+    fn if_implements_debug(#[case] source: &str) {
+        let (output, _) = run_test(source);
+        let out = output.stdout.str().to_string();
+
+        TestResults::new()
+            .fail("single_fail")
+            .fail("cases_fail::case_1")
+            .fail("cases_fail::case_2")
+            .fail("matrix_fail::u_1::s_1::t_1")
+            .fail("matrix_fail::u_1::s_1::t_2")
+            .fail("matrix_fail::u_1::s_2::t_1")
+            .fail("matrix_fail::u_1::s_2::t_2")
+            .fail("matrix_fail::u_2::s_1::t_1")
+            .fail("matrix_fail::u_2::s_1::t_2")
+            .fail("matrix_fail::u_1::s_2::t_1")
+            .fail("matrix_fail::u_2::s_2::t_2")
+            .assert(output);
+
+        assert_in!(out, "fu32 = 42");
+        assert_in!(out, r#"fstring = "A String""#);
+        assert_in!(out, r#"ftuple = (A, "A String", -12"#);
+
+        assert_in!(out, "u = 42");
+        assert_in!(out, r#"s = "str""#);
+        assert_in!(out, r#"t = ("ss", -12)"#);
+
+        assert_in!(out, "u = 24");
+        assert_in!(out, r#"s = "trs""#);
+        assert_in!(out, r#"t = ("tt", -24)"#);
+
+        assert_in!(out, "u = 1");
+        assert_in!(out, r#"s = "rst""#);
+        assert_in!(out, r#"t = ("SS", -12)"#);
+
+        assert_in!(out, "u = 2");
+        assert_in!(out, r#"s = "srt""#);
+        assert_in!(out, r#"t = ("TT", -24)"#);
+    }
+
+    #[rstest]
+    #[case::compact_syntax("dump_not_debug_compact.rs")]
+    #[case::attr_syntax("dump_not_debug.rs")]
+    fn should_not_compile_if_not_implement_debug(#[case] source: &str) {
+        let (output, name) = run_test(source);
+
+        assert_all_in!(
+            output.stderr.str(),
+            format!("--> {}/src/lib.rs:10:11", name),
+            "fn single(fixture: S) {}",
+            "^^^^^^^ `S` cannot be formatted using `{:?}`"
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!("--> {}/src/lib.rs:15:10", name),
+            "fn cases(s: S) {}",
+            "^ `S` cannot be formatted using `{:?}`"
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!("--> {}/src/lib.rs:20:11", name),
+            "fn matrix(s: S) {}",
+            "^ `S` cannot be formatted using `{:?}`"
+        );
+    }
+
+    #[rstest]
+    #[case::compact_syntax("dump_exclude_some_inputs_compact.rs")]
+    #[case::attr_syntax("dump_exclude_some_inputs.rs")]
+    fn can_exclude_some_inputs(#[case] source: &str) {
+        let (output, _) = run_test(source);
+        let out = output.stdout.str().to_string();
+
+        TestResults::new()
+            .fail("simple")
+            .fail("cases::case_1")
+            .fail("matrix::a_1::b_1::dd_1")
+            .assert(output);
+
+        assert_in!(out, "fu32 = 42");
+        assert_in!(out, "d = D");
+        assert_in!(out, "fd = D");
+        assert_in!(out, "dd = D");
+    }
+
+    #[test]
+    fn should_be_enclosed_in_an_explicit_session() {
+        let (output, _) = run_test(Path::new("single").join("dump_debug.rs"));
+        let out = output.stdout.str().to_string();
+
+        TestResults::new().fail("should_fail").assert(output);
+
+        let lines = out
+            .lines()
+            .skip_while(|l| !l.contains("TEST ARGUMENTS"))
+            .take_while(|l| !l.contains("TEST START"))
+            .collect::<Vec<_>>();
+
+        assert_eq!(
+            4,
+            lines.len(),
+            "Not contains 3 lines but {}: '{}'",
+            lines.len(),
+            lines.join("\n")
+        );
+    }
+}
+
+mod single {
+    use super::*;
+
+    fn res(name: impl AsRef<Path>) -> impl AsRef<Path> {
+        Path::new("single").join(name.as_ref())
+    }
+
+    #[test]
+    fn one_success_and_one_fail() {
+        let (output, _) = run_test(res("simple.rs"));
+
+        TestResults::new()
+            .ok("should_success")
+            .fail("should_fail")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_resolve_generics_fixture_outputs() {
+        let (output, _) = run_test(res("resolve.rs"));
+
+        TestResults::new()
+            .ok("generics_u32")
+            .ok("generics_i32")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_apply_partial_fixture() {
+        let (output, _) = run_test(res("partial.rs"));
+
+        TestResults::new()
+            .ok("default")
+            .ok("partial_1")
+            .ok("partial_attr_1")
+            .ok("partial_2")
+            .ok("partial_attr_2")
+            .ok("complete")
+            .ok("complete_attr")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_run_async_function() {
+        let prj = prj(res("async.rs"));
+        prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("should_pass")
+            .fail("should_fail")
+            .ok("should_panic_pass")
+            .fail("should_panic_fail")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_use_injected_test_attr() {
+        let prj = prj(res("inject.rs"));
+        prj.add_dependency("actix-rt", r#""1.1.0""#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("sync_case")
+            .ok("sync_case_panic")
+            .fail("sync_case_fail")
+            .fail("sync_case_panic_fail")
+            .ok("async_case")
+            .ok("async_case_panic")
+            .fail("async_case_fail")
+            .fail("async_case_panic_fail")
+            .assert(output);
+    }
+}
+
+mod cases {
+    use super::*;
+
+    fn res(name: impl AsRef<Path>) -> impl AsRef<Path> {
+        Path::new("cases").join(name.as_ref())
+    }
+
+    #[test]
+    fn should_compile() {
+        let output = prj(res("simple.rs")).compile().unwrap();
+
+        assert_eq!(
+            Some(0),
+            output.status.code(),
+            "Compile error due: {}",
+            output.stderr.str()
+        )
+    }
+
+    #[test]
+    fn happy_path() {
+        let (output, _) = run_test(res("simple.rs"));
+
+        TestResults::new()
+            .ok("strlen_test::case_1")
+            .ok("strlen_test::case_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn use_attr() {
+        let (output, _) = run_test(res("use_attr.rs"));
+
+        TestResults::new()
+            .ok("all::case_1_ciao")
+            .ok("all::case_2_panic")
+            .ok("all::case_3_foo")
+            .ok("just_cases::case_1_ciao")
+            .ok("just_cases::case_2_foo")
+            .ok("just_cases::case_3_panic")
+            .ok("just_args::case_1_ciao")
+            .ok("just_args::case_2_foo")
+            .ok("just_args::case_3_panic")
+            .ok("all_panic::case_1")
+            .ok("all_panic::case_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn case_description() {
+        let (output, _) = run_test(res("description.rs"));
+
+        TestResults::new()
+            .ok("description::case_1_user_test_description")
+            .ok("description::case_2")
+            .fail("description::case_3_user_test_description_fail")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_apply_partial_fixture() {
+        let (output, _) = run_test(res("partial.rs"));
+
+        TestResults::new()
+            .ok("default::case_1")
+            .ok("partial_1::case_1")
+            .ok("partial_2::case_1")
+            .ok("complete::case_1")
+            .ok("partial_attr_1::case_1")
+            .ok("partial_attr_2::case_1")
+            .ok("complete_attr::case_1")
+            .fail("default::case_2")
+            .fail("partial_1::case_2")
+            .fail("partial_2::case_2")
+            .fail("complete::case_2")
+            .fail("partial_attr_1::case_2")
+            .fail("partial_attr_2::case_2")
+            .fail("complete_attr::case_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_use_case_attributes() {
+        let (output, _) = run_test(res("case_attributes.rs"));
+
+        TestResults::new()
+            .ok("attribute_per_case::case_1_no_panic")
+            .ok("attribute_per_case::case_2_panic")
+            .ok("attribute_per_case::case_3_panic_with_message")
+            .fail("attribute_per_case::case_4_no_panic_but_fail")
+            .fail("attribute_per_case::case_5_panic_but_fail")
+            .fail("attribute_per_case::case_6_panic_with_wrong_message")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_run_async_function() {
+        let prj = prj(res("async.rs"));
+        prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("my_async_test::case_1_pass")
+            .fail("my_async_test::case_2_fail")
+            .ok("my_async_test::case_3_pass_panic")
+            .fail("my_async_test::case_4_fail_panic")
+            .ok("my_async_test_revert::case_1_pass")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_use_injected_test_attr() {
+        let prj = prj(res("inject.rs"));
+        prj.add_dependency("actix-rt", r#""1.1.0""#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("sync::case_1_pass")
+            .ok("sync::case_2_panic")
+            .fail("sync::case_3_fail")
+            .ok("fn_async::case_1_pass")
+            .ok("fn_async::case_2_panic")
+            .fail("fn_async::case_3_fail")
+            .assert(output);
+    }
+
+    #[test]
+    fn trace_just_one_test() {
+        let (output, _) = run_test(res("dump_just_one_case.rs"));
+        let out = output.stdout.str().to_string();
+
+        TestResults::new()
+            .fail("cases::case_1_first_no_dump")
+            .fail("cases::case_2_dump_me")
+            .fail("cases::case_3_last_no_dump")
+            .assert(output);
+
+        assert_in!(out, r#"s = "Trace it!""#);
+        assert_not_in!(out, r#"s = "Please don't trace me""#);
+    }
+
+    mod not_compile_if_missed_arguments {
+        use super::*;
+
+        #[test]
+        fn happy_path() {
+            let (output, _) = run_test(res("missed_argument.rs"));
+            let stderr = output.stderr.str();
+
+            assert_ne!(Some(0), output.status.code());
+            assert_in!(stderr, "Missed argument");
+            assert_in!(
+                stderr,
+                "
+                  |
+                4 | #[rstest(f, case(42), case(24))]
+                  |          ^
+                "
+                .unindent()
+            );
+        }
+
+        #[test]
+        fn should_reports_all() {
+            let (output, _) = run_test(res("missed_some_arguments.rs"));
+            let stderr = output.stderr.str();
+
+            assert_in!(
+                stderr,
+                "
+                  |
+                4 | #[rstest(a,b,c, case(1,2,3), case(3,2,1))]
+                  |          ^
+                "
+                .unindent()
+            );
+            assert_in!(
+                stderr,
+                "
+                  |
+                4 | #[rstest(a,b,c, case(1,2,3), case(3,2,1))]
+                  |              ^
+                "
+                .unindent()
+            );
+
+            assert_eq!(
+                2,
+                stderr.count("Missed argument"),
+                "Should contain message exactly 2 occurrences in error message:\n{}",
+                stderr
+            )
+        }
+
+        #[test]
+        fn should_report_just_one_error_message_for_all_test_cases() {
+            let (output, _) = run_test(res("missed_argument.rs"));
+            let stderr = output.stderr.str();
+
+            assert_eq!(
+                1,
+                stderr.count("Missed argument"),
+                "More than one message occurrence in error message:\n{}",
+                stderr
+            )
+        }
+
+        #[test]
+        fn should_not_report_error_in_macro_syntax() {
+            let (output, _) = run_test(res("missed_argument.rs"));
+            let stderr = output.stderr.str();
+
+            assert!(!stderr.contains("macros that expand to items"));
+        }
+    }
+
+    mod not_compile_if_a_case_has_a_wrong_signature {
+        use std::process::Output;
+
+        use lazy_static::lazy_static;
+
+        use super::*;
+
+        //noinspection RsTypeCheck
+        fn execute() -> &'static (Output, String) {
+            lazy_static! {
+                static ref OUTPUT: (Output, String) = run_test(res("case_with_wrong_args.rs"));
+            }
+            assert_ne!(Some(0), OUTPUT.0.status.code(), "Should not compile");
+            &OUTPUT
+        }
+
+        #[test]
+        fn with_too_much_arguments() {
+            let (output, _) = execute();
+            let stderr = output.stderr.str();
+
+            assert_in!(
+                stderr,
+                "
+                  |
+                8 | #[rstest(a, case(42, 43), case(12), case(24, 34))]
+                  |                  ^^^^^^
+                "
+                .unindent()
+            );
+
+            assert_in!(
+                stderr,
+                "
+                  |
+                8 | #[rstest(a, case(42, 43), case(12), case(24, 34))]
+                  |                                          ^^^^^^
+                "
+                .unindent()
+            );
+        }
+
+        #[test]
+        fn with_less_arguments() {
+            let (output, _) = execute();
+            let stderr = output.stderr.str();
+
+            assert_in!(
+                stderr,
+                "
+                  |
+                4 | #[rstest(a, b, case(42), case(1, 2), case(43))]
+                  |                     ^^
+                "
+                .unindent()
+            );
+
+            assert_in!(
+                stderr,
+                "
+                  |
+                4 | #[rstest(a, b, case(42), case(1, 2), case(43))]
+                  |                                           ^^
+                "
+                .unindent()
+            );
+        }
+
+        #[test]
+        fn and_reports_all_errors() {
+            let (output, _) = execute();
+            let stderr = output.stderr.str();
+
+            // Exactly 4 cases are wrong
+            assert_eq!(
+                4,
+                stderr.count("Wrong case signature: should match the given parameters list."),
+                "Should contain message exactly 4 occurrences in error message:\n{}",
+                stderr
+            );
+        }
+    }
+
+    mod not_compile_if_args_but_no_cases {
+        use std::process::Output;
+
+        use lazy_static::lazy_static;
+
+        use super::*;
+
+        //noinspection RsTypeCheck
+        fn execute() -> &'static (Output, String) {
+            lazy_static! {
+                static ref OUTPUT: (Output, String) = run_test(res("args_with_no_cases.rs"));
+            }
+            assert_ne!(Some(0), OUTPUT.0.status.code(), "Should not compile");
+            &OUTPUT
+        }
+
+        #[test]
+        fn report_error() {
+            let (output, name) = execute();
+            let stderr = output.stderr.str();
+
+            assert_in!(
+                stderr,
+                format!(
+                    "
+                error: No cases for this argument.
+                 --> {}/src/lib.rs:3:10
+                  |
+                3 | #[rstest(one, two, three)]
+                  |          ^^^
+                ",
+                    name
+                )
+                .unindent()
+            );
+        }
+
+        #[test]
+        fn and_reports_all_errors() {
+            let (output, _) = execute();
+            let stderr = output.stderr.str();
+
+            // Exactly 3 cases are wrong
+            assert_eq!(
+                3,
+                stderr.count("No cases for this argument."),
+                "Should contain message exactly 3 occurrences in error message:\n{}",
+                stderr
+            );
+        }
+    }
+}
+
+mod matrix {
+    use super::*;
+
+    fn res(name: impl AsRef<Path>) -> impl AsRef<Path> {
+        Path::new("matrix").join(name.as_ref())
+    }
+
+    #[test]
+    fn should_compile() {
+        let output = prj(res("simple.rs")).compile().unwrap();
+
+        assert_eq!(
+            Some(0),
+            output.status.code(),
+            "Compile error due: {}",
+            output.stderr.str()
+        )
+    }
+
+    #[test]
+    fn happy_path() {
+        let (output, _) = run_test(res("simple.rs"));
+
+        TestResults::new()
+            .ok("strlen_test::expected_1::input_1")
+            .ok("strlen_test::expected_1::input_2")
+            .ok("strlen_test::expected_2::input_1")
+            .ok("strlen_test::expected_2::input_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_apply_partial_fixture() {
+        let (output, _) = run_test(res("partial.rs"));
+
+        TestResults::new()
+            .ok("default::a_1::b_1")
+            .ok("default::a_1::b_2")
+            .ok("default::a_2::b_1")
+            .ok("partial_2::a_2::b_2")
+            .ok("partial_attr_2::a_2::b_2")
+            .ok("complete::a_2::b_2")
+            .ok("complete_attr::a_2::b_2")
+            .fail("default::a_2::b_2")
+            .fail("partial_1::a_1::b_1")
+            .fail("partial_1::a_1::b_2")
+            .fail("partial_1::a_2::b_1")
+            .fail("partial_1::a_2::b_2")
+            .fail("partial_2::a_1::b_1")
+            .fail("partial_2::a_1::b_2")
+            .fail("partial_2::a_2::b_1")
+            .fail("complete::a_1::b_1")
+            .fail("complete::a_1::b_2")
+            .fail("complete::a_2::b_1")
+            .fail("partial_attr_1::a_1::b_1")
+            .fail("partial_attr_1::a_1::b_2")
+            .fail("partial_attr_1::a_2::b_1")
+            .fail("partial_attr_1::a_2::b_2")
+            .fail("partial_attr_2::a_1::b_1")
+            .fail("partial_attr_2::a_1::b_2")
+            .fail("partial_attr_2::a_2::b_1")
+            .fail("complete_attr::a_1::b_1")
+            .fail("complete_attr::a_1::b_2")
+            .fail("complete_attr::a_2::b_1")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_run_async_function() {
+        let prj = prj(res("async.rs"));
+        prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("my_async_test::first_1::second_1")
+            .fail("my_async_test::first_1::second_2")
+            .fail("my_async_test::first_2::second_1")
+            .ok("my_async_test::first_2::second_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn should_use_injected_test_attr() {
+        let prj = prj(res("inject.rs"));
+        prj.add_dependency("actix-rt", r#""1.1.0""#);
+
+        let output = prj.run_tests().unwrap();
+
+        TestResults::new()
+            .ok("sync::first_1::second_1")
+            .fail("sync::first_1::second_2")
+            .fail("sync::first_2::second_1")
+            .ok("sync::first_2::second_2")
+            .ok("fn_async::first_1::second_1")
+            .fail("fn_async::first_1::second_2")
+            .fail("fn_async::first_2::second_1")
+            .ok("fn_async::first_2::second_2")
+            .assert(output);
+    }
+
+    #[test]
+    fn use_args_attributes() {
+        let (output, _) = run_test(res("use_attr.rs"));
+
+        TestResults::new()
+            .ok("both::expected_1::input_1")
+            .ok("both::expected_1::input_2")
+            .ok("both::expected_2::input_1")
+            .ok("both::expected_2::input_2")
+            .ok("first::input_1::expected_1")
+            .ok("first::input_2::expected_1")
+            .ok("first::input_1::expected_2")
+            .ok("first::input_2::expected_2")
+            .ok("second::expected_1::input_1")
+            .ok("second::expected_1::input_2")
+            .ok("second::expected_2::input_1")
+            .ok("second::expected_2::input_2")
+            .assert(output);
+    }
+}
+
+#[test]
+fn convert_string_literal() {
+    let (output, _) = run_test("convert_string_literal.rs");
+
+    assert_in!(output.stdout.str(), "Cannot parse 'error' to get MyType");
+
+    TestResults::new()
+        .ok("cases::case_1")
+        .ok("cases::case_2")
+        .ok("cases::case_3")
+        .ok("cases::case_4")
+        .fail("cases::case_5")
+        .fail("cases::case_6")
+        .ok("values::addr_1")
+        .ok("values::addr_2")
+        .fail("values::addr_3")
+        .fail("values::addr_4")
+        .ok("not_convert_byte_array::case_1::values_1")
+        .ok("not_convert_impl::case_1")
+        .ok("not_convert_generics::case_1")
+        .ok("not_convert_generics::case_2")
+        .ok("convert_without_debug::case_1")
+        .fail("convert_without_debug::case_2")
+        .assert(output);
+}
+
+#[test]
+fn happy_path() {
+    let (output, _) = run_test("happy_path.rs");
+
+    TestResults::new()
+        .ok("happy::case_1::expected_1::input_1")
+        .ok("happy::case_1::expected_1::input_2")
+        .ok("happy::case_1::expected_2::input_1")
+        .ok("happy::case_1::expected_2::input_2")
+        .ok("happy::case_2_second::expected_1::input_1")
+        .ok("happy::case_2_second::expected_1::input_2")
+        .ok("happy::case_2_second::expected_2::input_1")
+        .ok("happy::case_2_second::expected_2::input_2")
+        .assert(output);
+}
+
+#[test]
+fn rename() {
+    let (output, _) = run_test("rename.rs");
+
+    TestResults::new()
+        .ok("compact")
+        .ok("compact_injected")
+        .ok("attribute")
+        .ok("attribute_injected")
+        .assert(output);
+}
+
+#[test]
+fn ignore_underscore_args() {
+    let (output, _) = run_test("ignore_args.rs");
+
+    TestResults::new()
+        .ok("test::case_1::_ignore3_1")
+        .ok("test::case_1::_ignore3_2")
+        .ok("test::case_1::_ignore3_3")
+        .ok("test::case_1::_ignore3_4")
+        .ok("test::case_2::_ignore3_1")
+        .ok("test::case_2::_ignore3_2")
+        .ok("test::case_2::_ignore3_3")
+        .ok("test::case_2::_ignore3_4")
+        .assert(output);
+}
+
+mod should_show_correct_errors {
+    use std::process::Output;
+
+    use lazy_static::lazy_static;
+
+    use super::*;
+
+    //noinspection RsTypeCheck
+    fn execute() -> &'static (Output, String) {
+        lazy_static! {
+            static ref OUTPUT: (Output, String) = run_test("errors.rs");
+        }
+        &OUTPUT
+    }
+
+    #[test]
+    fn if_no_fixture() {
+        let (output, name) = execute();
+
+        assert_in!(output.stderr.str(), "error[E0433]: ");
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                  --> {}/src/lib.rs:13:33
+                   |
+                13 | fn error_cannot_resolve_fixture(no_fixture: u32, f: u32) {{}}",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_inject_wrong_fixture() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Missed argument: 'not_a_fixture' should be a test function argument.
+                  --> {}/src/lib.rs:28:23
+                   |
+                28 | #[rstest(f, case(42), not_a_fixture(24))]
+                   |                       ^^^^^^^^^^^^^
+                ",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_wrong_type() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                r#"
+                error[E0308]: mismatched types
+                 --> {}/src/lib.rs:9:18
+                  |
+                9 |     let a: u32 = "";
+                "#,
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_wrong_type_fixture() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:16:29
+                   |
+                16 | fn error_fixture_wrong_type(fixture: String, f: u32) {{}}
+                   |                             ^^^^^^^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_wrong_type_case_param() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:19:26
+                   |
+                19 | fn error_case_wrong_type(f: &str) {{}}",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_wrong_type_matrix_param() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:51:28
+                   |
+                51 | fn error_matrix_wrong_type(f: &str) {{}}",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_arbitrary_rust_code_has_some_errors() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:22:31
+                   |
+                22 |     case(vec![1,2,3].contains(2)))
+                   |                               ^
+                   |                               |",
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error[E0308]: mismatched types
+                  --> {}/src/lib.rs:53:45
+                   |
+                53 | #[rstest(condition => [vec![1,2,3].contains(2)] )]
+                   |                                             ^
+                   |                                             |",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_inject_a_fixture_that_is_already_a_case() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:41:13
+                   |
+                41 | #[rstest(f, f(42), case(12))]
+                   |             ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_a_case_arg_that_is_already_an_injected_fixture() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:44:17
+                   |
+                44 | #[rstest(f(42), f, case(12))]
+                   |                 ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_inject_a_fixture_more_than_once() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:47:20
+                   |
+                47 | #[rstest(v, f(42), f(42), case(12))]
+                   |                    ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_list_argument_dont_match_function_signature() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Missed argument: 'not_exist_1' should be a test function argument.
+                  --> {}/src/lib.rs:61:10
+                   |
+                61 | #[rstest(not_exist_1 => [42],
+                   |          ^^^^^^^^^^^",
+                name
+            )
+            .unindent()
+        );
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Missed argument: 'not_exist_2' should be a test function argument.
+                  --> {}/src/lib.rs:62:10
+                   |
+                62 |          not_exist_2 => [42])]
+                   |          ^^^^^^^^^^^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_inject_a_fixture_that_is_already_a_value_list() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:65:25
+                   |
+                65 | #[rstest(f => [41, 42], f(42))]
+                   |                         ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_value_list_more_that_once() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'a' is already defined.
+                  --> {}/src/lib.rs:77:25
+                   |
+                77 | #[rstest(a => [42, 24], a => [24, 42])]
+                   |                         ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_value_list_that_is_already_an_injected_fixture() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'f' is already defined.
+                  --> {}/src/lib.rs:68:17
+                   |
+                68 | #[rstest(f(42), f => [41, 42])]
+                   |                 ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_value_list_that_is_already_a_case_arg() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'a' is already defined.
+                  --> {}/src/lib.rs:71:23
+                   |
+                71 | #[rstest(a, case(42), a => [42])]
+                   |                       ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_a_case_arg_that_is_already_a_value_list() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'a' is already defined.
+                  --> {}/src/lib.rs:74:21
+                   |
+                74 | #[rstest(a => [42], a, case(42))]
+                   |                     ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_define_a_case_arg_more_that_once() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Duplicate argument: 'a' is already defined.
+                  --> {}/src/lib.rs:80:13
+                   |
+                80 | #[rstest(a, a, case(42))]
+                   |             ^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_a_value_contains_empty_list() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                error: Values list should not be empty
+                  --> {}/src/lib.rs:58:19
+                   |
+                58 | #[rstest(empty => [])]
+                   |                   ^^",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_try_to_convert_literal_string_to_a_type_that_not_implement_from_str() {
+        let (output, name) = execute();
+
+        assert_in!(output.stderr.str(), format!("--> {}/src/lib.rs:84:1", name));
+        assert_in!(
+            output.stderr.str(),
+            "| --------- doesn't satisfy `S: FromStr`"
+        );
+    }
+
+    #[test]
+    fn if_try_to_use_future_on_an_impl() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                  --> {}/src/lib.rs:90:57
+                   |
+                90 | async fn error_future_on_impl_type(#[case] #[future] s: impl AsRef<str>) {{}}
+                   |                                                         ^^^^^^^^^^^^^^^
+                ",
+                name
+            )
+            .unindent()
+        );
+    }
+
+    #[test]
+    fn if_try_to_use_future_more_that_once() {
+        let (output, name) = execute();
+
+        assert_in!(
+            output.stderr.str(),
+            format!(
+                "
+                  --> {}/src/lib.rs:94:54
+                   |
+                94 | async fn error_future_on_impl_type(#[case] #[future] #[future] a: i32) {{}}
+                   |                                                      ^^^^^^^^^
+                ",
+                name
+            )
+            .unindent()
+        );
+    }
+}
diff --git a/third_party/rust/rustc_version/v0_4/BUILD.gn b/third_party/rust/rustc_version/v0_4/BUILD.gn
new file mode 100644
index 0000000..2b6cb374
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2022 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("//build/rust/cargo_crate.gni")
+
+cargo_crate("buildrs_support") {
+  crate_name = "rustc_version"
+  epoch = "0.4"
+  crate_type = "rlib"
+
+  # Only for usage from third-party crates. Add the crate to
+  # third_party.toml to use it from first-party code.
+  visibility = [ "//third_party/rust/*" ]
+  crate_root = "crate/src/lib.rs"
+  skip_unit_tests = true
+  sources = [ "crate/src/lib.rs" ]
+  edition = "2018"
+  deps = [ "//third_party/rust/semver/v1:lib" ]
+}
diff --git a/third_party/rust/rustc_version/v0_4/README.chromium b/third_party/rust/rustc_version/v0_4/README.chromium
new file mode 100644
index 0000000..202ff09
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/README.chromium
@@ -0,0 +1,6 @@
+Name: rustc_version
+URL: https://crates.io/crates/rustc_version
+Description: A library for querying the version of a installed rustc compiler
+Version: 0.4.0
+Security Critical: no
+License: Apache 2.0
diff --git a/third_party/rust/rustc_version/v0_4/crate/.cargo_vcs_info.json b/third_party/rust/rustc_version/v0_4/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..c0d69af
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "6093aa9de6428df2e1c04aa9e969f1af7c34664f"
+  }
+}
diff --git a/third_party/rust/rustc_version/v0_4/crate/.github/dependabot.yml b/third_party/rust/rustc_version/v0_4/crate/.github/dependabot.yml
new file mode 100644
index 0000000..93a4164
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+- package-ecosystem: cargo
+  directory: "/"
+  schedule:
+    interval: daily
+    time: "04:00"
+  open-pull-requests-limit: 10
+  ignore:
+  - dependency-name: semver
+    versions:
+    - "> 1.0, < 2"
diff --git a/third_party/rust/rustc_version/v0_4/crate/.github/workflows/rust.yml b/third_party/rust/rustc_version/v0_4/crate/.github/workflows/rust.yml
new file mode 100644
index 0000000..6a1c4f8
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/.github/workflows/rust.yml
@@ -0,0 +1,70 @@
+name: CI
+
+on:
+  push:
+    branches: ['master']
+  pull_request:
+
+jobs:
+  test:
+    strategy:
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+        rust: [stable, beta, nightly, 1.32.0]
+        exclude:
+          - os: macos-latest
+            rust: beta
+          - os: windows-latest
+            rust: beta
+          - os: macos-latest
+            rust: nightly
+          - os: windows-latest
+            rust: nightly
+          - os: macos-latest
+            rust: 1.32.0
+          - os: windows-latest
+            rust: 1.32.0
+
+    runs-on: ${{ matrix.os }}
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: ${{ matrix.rust }}
+          override: true
+      - uses: actions-rs/cargo@v1
+        with:
+          command: build
+          args: --all-features --all-targets
+      - uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --all-features
+
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+          components: rustfmt, clippy
+      - uses: actions-rs/cargo@v1
+        with:
+          command: fmt
+          args: --all -- --check
+      - uses: actions-rs/cargo@v1
+        if: always()
+        with:
+          command: clippy
+          args: --all-targets --all-features -- -D warnings
+
+  audit:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: EmbarkStudios/cargo-deny-action@v1
diff --git a/third_party/rust/rustc_version/v0_4/crate/.gitignore b/third_party/rust/rustc_version/v0_4/crate/.gitignore
new file mode 100644
index 0000000..22d9b57
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/.gitignore
@@ -0,0 +1,3 @@
+target
+Cargo.lock
+.DS_Store
diff --git a/third_party/rust/rustc_version/v0_4/crate/Cargo.toml b/third_party/rust/rustc_version/v0_4/crate/Cargo.toml
new file mode 100644
index 0000000..c81ff85
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/Cargo.toml
@@ -0,0 +1,27 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "rustc_version"
+version = "0.4.0"
+authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Marvin Löbel <loebel.marvin@gmail.com>"]
+description = "A library for querying the version of a installed rustc compiler"
+documentation = "https://docs.rs/rustc_version/"
+readme = "README.md"
+keywords = ["version", "rustc"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/Kimundi/rustc-version-rs"
+[dependencies.semver]
+version = "1.0"
+[dev-dependencies.doc-comment]
+version = "0.3"
diff --git a/third_party/rust/rustc_version/v0_4/crate/Cargo.toml.orig b/third_party/rust/rustc_version/v0_4/crate/Cargo.toml.orig
new file mode 100644
index 0000000..464176fa
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/Cargo.toml.orig
@@ -0,0 +1,17 @@
+[package]
+name = "rustc_version"
+version = "0.4.0"
+authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Marvin Löbel <loebel.marvin@gmail.com>"]
+license = "MIT/Apache-2.0"
+description = "A library for querying the version of a installed rustc compiler"
+readme = "README.md"
+documentation = "https://docs.rs/rustc_version/"
+repository = "https://github.com/Kimundi/rustc-version-rs"
+keywords = ["version", "rustc"]
+edition = "2018"
+
+[dependencies]
+semver = "1.0"
+
+[dev-dependencies]
+doc-comment = "0.3"
diff --git a/third_party/rust/rustc_version/v0_4/crate/LICENSE-APACHE b/third_party/rust/rustc_version/v0_4/crate/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/rust/rustc_version/v0_4/crate/LICENSE-MIT b/third_party/rust/rustc_version/v0_4/crate/LICENSE-MIT
new file mode 100644
index 0000000..40b8817
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2016 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/rustc_version/v0_4/crate/README.md b/third_party/rust/rustc_version/v0_4/crate/README.md
new file mode 100644
index 0000000..e76ef95
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/README.md
@@ -0,0 +1,81 @@
+rustc-version-rs
+================
+
+[![Documentation](https://docs.rs/rustc_version/badge.svg)](https://docs.rs/rustc_version/)
+[![Crates.io](https://img.shields.io/crates/v/rustc_version.svg)](https://crates.io/crates/rustc_version)
+[![Build status](https://github.com/Kimundi/rustc-version-rs/workflows/CI/badge.svg)](https://github.com/Kimundi/rustc-version-rs/actions?query=workflow%3ACI)
+
+A library for querying the version of a `rustc` compiler.
+
+This can be used by build scripts or other tools dealing with Rust sources
+to make decisions based on the version of the compiler. Current MSRV is 1.32.0.
+
+If this is of interest, also consider looking at these other crates:
+
+* [autocfg](https://crates.io/crates/autocfg/), which helps with feature detection instead of depending on compiler versions
+* [rustversion](https://github.com/dtolnay/rustversion) provides a procedural macro with no other dependencies
+
+# Getting Started
+
+[rustc-version-rs is available on crates.io](https://crates.io/crates/rustc_version).
+It is recommended to look there for the newest released version, as well as links to the newest builds of the docs.
+
+At the point of the last update of this README, the latest published version could be used like this:
+
+Add the following dependency to your Cargo manifest...
+
+```toml
+[build-dependencies]
+rustc_version = "0.2"
+```
+
+... and see the [docs](https://docs.rs/rustc_version) for how to use it.
+
+# Example
+
+```rust
+// This could be a cargo build script
+
+use rustc_version::{version, version_meta, Channel, Version};
+
+fn main() {
+    // Assert we haven't travelled back in time
+    assert!(version().unwrap().major >= 1);
+
+    // Set cfg flags depending on release channel
+    match version_meta().unwrap().channel {
+        Channel::Stable => {
+            println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
+        }
+        Channel::Beta => {
+            println!("cargo:rustc-cfg=RUSTC_IS_BETA");
+        }
+        Channel::Nightly => {
+            println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY");
+        }
+        Channel::Dev => {
+            println!("cargo:rustc-cfg=RUSTC_IS_DEV");
+        }
+    }
+
+    // Check for a minimum version
+    if version().unwrap() >= Version::parse("1.4.0").unwrap() {
+        println!("cargo:rustc-cfg=compiler_has_important_bugfix");
+    }
+}
+```
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
+additional terms or conditions.
diff --git a/third_party/rust/rustc_version/v0_4/crate/deny.toml b/third_party/rust/rustc_version/v0_4/crate/deny.toml
new file mode 100644
index 0000000..38c47a8dc
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/deny.toml
@@ -0,0 +1,3 @@
+[licenses]
+allow-osi-fsf-free = "either"
+copyleft = "deny"
diff --git a/third_party/rust/rustc_version/v0_4/crate/src/lib.rs b/third_party/rust/rustc_version/v0_4/crate/src/lib.rs
new file mode 100644
index 0000000..cee1ec8
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/src/lib.rs
@@ -0,0 +1,417 @@
+// Copyright 2016 rustc-version-rs developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![warn(missing_docs)]
+
+//! Simple library for getting the version information of a `rustc`
+//! compiler.
+//!
+//! This can be used by build scripts or other tools dealing with Rust sources
+//! to make decisions based on the version of the compiler.
+//!
+//! It calls `$RUSTC --version -v` and parses the output, falling
+//! back to `rustc` if `$RUSTC` is not set.
+//!
+//! # Example
+//!
+//! ```rust
+//! // This could be a cargo build script
+//!
+//! use rustc_version::{version, version_meta, Channel, Version};
+//!
+//! // Assert we haven't travelled back in time
+//! assert!(version().unwrap().major >= 1);
+//!
+//! // Set cfg flags depending on release channel
+//! match version_meta().unwrap().channel {
+//!     Channel::Stable => {
+//!         println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
+//!     }
+//!     Channel::Beta => {
+//!         println!("cargo:rustc-cfg=RUSTC_IS_BETA");
+//!     }
+//!     Channel::Nightly => {
+//!         println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY");
+//!     }
+//!     Channel::Dev => {
+//!         println!("cargo:rustc-cfg=RUSTC_IS_DEV");
+//!     }
+//! }
+//!
+//! // Check for a minimum version
+//! if version().unwrap() >= Version::parse("1.4.0").unwrap() {
+//!     println!("cargo:rustc-cfg=compiler_has_important_bugfix");
+//! }
+//! ```
+
+#[cfg(test)]
+#[macro_use]
+extern crate doc_comment;
+
+#[cfg(test)]
+doctest!("../README.md");
+
+use std::collections::HashMap;
+use std::process::Command;
+use std::{env, error, fmt, io, num, str};
+use std::{ffi::OsString, str::FromStr};
+
+// Convenience re-export to allow version comparison without needing to add
+// semver crate.
+pub use semver::Version;
+
+use Error::*;
+
+/// Release channel of the compiler.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
+pub enum Channel {
+    /// Development release channel
+    Dev,
+    /// Nightly release channel
+    Nightly,
+    /// Beta release channel
+    Beta,
+    /// Stable release channel
+    Stable,
+}
+
+/// LLVM version
+///
+/// LLVM's version numbering scheme is not semver compatible until version 4.0
+///
+/// rustc [just prints the major and minor versions], so other parts of the version are not included.
+///
+/// [just prints the major and minor versions]: https://github.com/rust-lang/rust/blob/b5c9e2448c9ace53ad5c11585803894651b18b0a/compiler/rustc_codegen_llvm/src/llvm_util.rs#L173-L178
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct LlvmVersion {
+    // fields must be ordered major, minor for comparison to be correct
+    /// Major version
+    pub major: u64,
+    /// Minor version
+    pub minor: u64,
+    // TODO: expose micro version here
+}
+
+impl fmt::Display for LlvmVersion {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}.{}", self.major, self.minor)
+    }
+}
+
+impl FromStr for LlvmVersion {
+    type Err = LlvmVersionParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut parts = s
+            .split('.')
+            .map(|part| -> Result<u64, LlvmVersionParseError> {
+                if part == "0" {
+                    Ok(0)
+                } else if part.starts_with('0') {
+                    Err(LlvmVersionParseError::ComponentMustNotHaveLeadingZeros)
+                } else if part.starts_with('-') || part.starts_with('+') {
+                    Err(LlvmVersionParseError::ComponentMustNotHaveSign)
+                } else {
+                    Ok(part.parse()?)
+                }
+            });
+
+        let major = parts.next().unwrap()?;
+        let mut minor = 0;
+
+        if let Some(part) = parts.next() {
+            minor = part?;
+        } else if major < 4 {
+            // LLVM versions earlier than 4.0 have significant minor versions, so require the minor version in this case.
+            return Err(LlvmVersionParseError::MinorVersionRequiredBefore4);
+        }
+
+        if let Some(Err(e)) = parts.next() {
+            return Err(e);
+        }
+
+        if parts.next().is_some() {
+            return Err(LlvmVersionParseError::TooManyComponents);
+        }
+
+        Ok(Self { major, minor })
+    }
+}
+
+/// Rustc version plus metadata like git short hash and build date.
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct VersionMeta {
+    /// Version of the compiler
+    pub semver: Version,
+
+    /// Git short hash of the build of the compiler
+    pub commit_hash: Option<String>,
+
+    /// Commit date of the compiler
+    pub commit_date: Option<String>,
+
+    /// Build date of the compiler; this was removed between Rust 1.0.0 and 1.1.0.
+    pub build_date: Option<String>,
+
+    /// Release channel of the compiler
+    pub channel: Channel,
+
+    /// Host target triple of the compiler
+    pub host: String,
+
+    /// Short version string of the compiler
+    pub short_version_string: String,
+
+    /// Version of LLVM used by the compiler
+    pub llvm_version: Option<LlvmVersion>,
+}
+
+impl VersionMeta {
+    /// Returns the version metadata for `cmd`, which should be a `rustc` command.
+    pub fn for_command(mut cmd: Command) -> Result<VersionMeta> {
+        let out = cmd
+            .arg("-vV")
+            .output()
+            .map_err(Error::CouldNotExecuteCommand)?;
+
+        if !out.status.success() {
+            return Err(Error::CommandError {
+                stdout: String::from_utf8_lossy(&out.stdout).into(),
+                stderr: String::from_utf8_lossy(&out.stderr).into(),
+            });
+        }
+
+        version_meta_for(str::from_utf8(&out.stdout)?)
+    }
+}
+
+/// Returns the `rustc` SemVer version.
+pub fn version() -> Result<Version> {
+    Ok(version_meta()?.semver)
+}
+
+/// Returns the `rustc` SemVer version and additional metadata
+/// like the git short hash and build date.
+pub fn version_meta() -> Result<VersionMeta> {
+    let cmd = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
+
+    VersionMeta::for_command(Command::new(cmd))
+}
+
+/// Parses a "rustc -vV" output string and returns
+/// the SemVer version and additional metadata
+/// like the git short hash and build date.
+pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
+    let mut map = HashMap::new();
+    for (i, line) in verbose_version_string.lines().enumerate() {
+        if i == 0 {
+            map.insert("short", line);
+            continue;
+        }
+
+        let mut parts = line.splitn(2, ": ");
+        let key = match parts.next() {
+            Some(key) => key,
+            None => continue,
+        };
+
+        if let Some(value) = parts.next() {
+            map.insert(key, value);
+        }
+    }
+
+    let short_version_string = expect_key("short", &map)?;
+    let host = expect_key("host", &map)?;
+    let release = expect_key("release", &map)?;
+    let semver: Version = release.parse()?;
+
+    let channel = match semver.pre.split('.').next().unwrap() {
+        "" => Channel::Stable,
+        "dev" => Channel::Dev,
+        "beta" => Channel::Beta,
+        "nightly" => Channel::Nightly,
+        x => return Err(Error::UnknownPreReleaseTag(x.to_owned())),
+    };
+
+    let commit_hash = expect_key_or_unknown("commit-hash", &map)?;
+    let commit_date = expect_key_or_unknown("commit-date", &map)?;
+    let build_date = map
+        .get("build-date")
+        .filter(|&v| *v != "unknown")
+        .map(|&v| String::from(v));
+    let llvm_version = match map.get("LLVM version") {
+        Some(&v) => Some(v.parse()?),
+        None => None,
+    };
+
+    Ok(VersionMeta {
+        semver,
+        commit_hash,
+        commit_date,
+        build_date,
+        channel,
+        host,
+        short_version_string,
+        llvm_version,
+    })
+}
+
+fn expect_key_or_unknown(key: &str, map: &HashMap<&str, &str>) -> Result<Option<String>, Error> {
+    match map.get(key) {
+        Some(&v) if v == "unknown" => Ok(None),
+        Some(&v) => Ok(Some(String::from(v))),
+        None => Err(Error::UnexpectedVersionFormat),
+    }
+}
+
+fn expect_key(key: &str, map: &HashMap<&str, &str>) -> Result<String, Error> {
+    map.get(key)
+        .map(|&v| String::from(v))
+        .ok_or(Error::UnexpectedVersionFormat)
+}
+
+/// LLVM Version Parse Error
+#[derive(Debug)]
+pub enum LlvmVersionParseError {
+    /// An error occurred in parsing a version component as an integer
+    ParseIntError(num::ParseIntError),
+    /// A version component must not have leading zeros
+    ComponentMustNotHaveLeadingZeros,
+    /// A version component has a sign
+    ComponentMustNotHaveSign,
+    /// Minor version component must be zero on LLVM versions later than 4.0
+    MinorVersionMustBeZeroAfter4,
+    /// Minor version component is required on LLVM versions earlier than 4.0
+    MinorVersionRequiredBefore4,
+    /// Too many components
+    TooManyComponents,
+}
+
+impl From<num::ParseIntError> for LlvmVersionParseError {
+    fn from(e: num::ParseIntError) -> Self {
+        LlvmVersionParseError::ParseIntError(e)
+    }
+}
+
+impl fmt::Display for LlvmVersionParseError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            LlvmVersionParseError::ParseIntError(e) => {
+                write!(f, "error parsing LLVM version component: {}", e)
+            }
+            LlvmVersionParseError::ComponentMustNotHaveLeadingZeros => {
+                write!(f, "a version component must not have leading zeros")
+            }
+            LlvmVersionParseError::ComponentMustNotHaveSign => {
+                write!(f, "a version component must not have a sign")
+            }
+            LlvmVersionParseError::MinorVersionMustBeZeroAfter4 => write!(
+                f,
+                "LLVM's minor version component must be 0 for versions greater than 4.0"
+            ),
+            LlvmVersionParseError::MinorVersionRequiredBefore4 => write!(
+                f,
+                "LLVM's minor version component is required for versions less than 4.0"
+            ),
+            LlvmVersionParseError::TooManyComponents => write!(f, "too many version components"),
+        }
+    }
+}
+
+impl error::Error for LlvmVersionParseError {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match self {
+            LlvmVersionParseError::ParseIntError(e) => Some(e),
+            LlvmVersionParseError::ComponentMustNotHaveLeadingZeros
+            | LlvmVersionParseError::ComponentMustNotHaveSign
+            | LlvmVersionParseError::MinorVersionMustBeZeroAfter4
+            | LlvmVersionParseError::MinorVersionRequiredBefore4
+            | LlvmVersionParseError::TooManyComponents => None,
+        }
+    }
+}
+
+/// The error type for this crate.
+#[derive(Debug)]
+pub enum Error {
+    /// An error occurred while trying to find the `rustc` to run.
+    CouldNotExecuteCommand(io::Error),
+    /// Error output from the command that was run.
+    CommandError {
+        /// stdout output from the command
+        stdout: String,
+        /// stderr output from the command
+        stderr: String,
+    },
+    /// The output of `rustc -vV` was not valid utf-8.
+    Utf8Error(str::Utf8Error),
+    /// The output of `rustc -vV` was not in the expected format.
+    UnexpectedVersionFormat,
+    /// An error occurred in parsing the semver.
+    SemVerError(semver::Error),
+    /// The pre-release tag is unknown.
+    UnknownPreReleaseTag(String),
+    /// An error occurred in parsing a `LlvmVersion`.
+    LlvmVersionError(LlvmVersionParseError),
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            CouldNotExecuteCommand(ref e) => write!(f, "could not execute command: {}", e),
+            CommandError {
+                ref stdout,
+                ref stderr,
+            } => write!(
+                f,
+                "error from command -- stderr:\n\n{}\n\nstderr:\n\n{}",
+                stderr, stdout,
+            ),
+            Utf8Error(_) => write!(f, "invalid UTF-8 output from `rustc -vV`"),
+            UnexpectedVersionFormat => write!(f, "unexpected `rustc -vV` format"),
+            SemVerError(ref e) => write!(f, "error parsing version: {}", e),
+            UnknownPreReleaseTag(ref i) => write!(f, "unknown pre-release tag: {}", i),
+            LlvmVersionError(ref e) => write!(f, "error parsing LLVM's version: {}", e),
+        }
+    }
+}
+
+impl error::Error for Error {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match *self {
+            CouldNotExecuteCommand(ref e) => Some(e),
+            CommandError { .. } => None,
+            Utf8Error(ref e) => Some(e),
+            UnexpectedVersionFormat => None,
+            SemVerError(ref e) => Some(e),
+            UnknownPreReleaseTag(_) => None,
+            LlvmVersionError(ref e) => Some(e),
+        }
+    }
+}
+
+macro_rules! impl_from {
+    ($($err_ty:ty => $variant:ident),* $(,)*) => {
+        $(
+            impl From<$err_ty> for Error {
+                fn from(e: $err_ty) -> Error {
+                    Error::$variant(e)
+                }
+            }
+        )*
+    }
+}
+
+impl_from! {
+    str::Utf8Error => Utf8Error,
+    semver::Error => SemVerError,
+    LlvmVersionParseError => LlvmVersionError,
+}
+
+/// The result type for this crate.
+pub type Result<T, E = Error> = std::result::Result<T, E>;
diff --git a/third_party/rust/rustc_version/v0_4/crate/tests/all.rs b/third_party/rust/rustc_version/v0_4/crate/tests/all.rs
new file mode 100644
index 0000000..c3cff70
--- /dev/null
+++ b/third_party/rust/rustc_version/v0_4/crate/tests/all.rs
@@ -0,0 +1,456 @@
+#![allow(clippy::match_like_matches_macro)]
+
+use std::process::Command;
+
+use rustc_version::{
+    version, version_meta, version_meta_for, Channel, Error, LlvmVersion, LlvmVersionParseError,
+    Version, VersionMeta,
+};
+
+#[test]
+fn rustc_error() {
+    let mut cmd = Command::new("rustc");
+    cmd.arg("--FOO");
+    let stderr = match VersionMeta::for_command(cmd) {
+        Err(Error::CommandError { stdout: _, stderr }) => stderr,
+        _ => panic!("command error expected"),
+    };
+    assert_eq!(stderr, "error: Unrecognized option: \'FOO\'\n\n");
+}
+
+#[test]
+fn smoketest() {
+    let v = version().unwrap();
+    assert!(v.major >= 1);
+
+    let v = version_meta().unwrap();
+    assert!(v.semver.major >= 1);
+
+    assert!(version().unwrap() >= Version::parse("1.0.0").unwrap());
+}
+
+#[test]
+fn parse_1_0_0() {
+    let version = version_meta_for(
+        "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
+binary: rustc
+commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
+commit-date: 2015-05-13
+build-date: 2015-05-14
+host: x86_64-unknown-linux-gnu
+release: 1.0.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.0.0").unwrap());
+    assert_eq!(
+        version.commit_hash,
+        Some("a59de37e99060162a2674e3ff45409ac73595c0e".into())
+    );
+    assert_eq!(version.commit_date, Some("2015-05-13".into()));
+    assert_eq!(version.build_date, Some("2015-05-14".into()));
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "x86_64-unknown-linux-gnu");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)"
+    );
+    assert_eq!(version.llvm_version, None);
+}
+
+#[test]
+fn parse_unknown() {
+    let version = version_meta_for(
+        "rustc 1.3.0
+binary: rustc
+commit-hash: unknown
+commit-date: unknown
+host: x86_64-unknown-linux-gnu
+release: 1.3.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.3.0").unwrap());
+    assert_eq!(version.commit_hash, None);
+    assert_eq!(version.commit_date, None);
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "x86_64-unknown-linux-gnu");
+    assert_eq!(version.short_version_string, "rustc 1.3.0");
+    assert_eq!(version.llvm_version, None);
+}
+
+#[test]
+fn parse_nightly() {
+    let version = version_meta_for(
+        "rustc 1.5.0-nightly (65d5c0833 2015-09-29)
+binary: rustc
+commit-hash: 65d5c083377645a115c4ac23a620d3581b9562b6
+commit-date: 2015-09-29
+host: x86_64-unknown-linux-gnu
+release: 1.5.0-nightly",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.5.0-nightly").unwrap());
+    assert_eq!(
+        version.commit_hash,
+        Some("65d5c083377645a115c4ac23a620d3581b9562b6".into())
+    );
+    assert_eq!(version.commit_date, Some("2015-09-29".into()));
+    assert_eq!(version.channel, Channel::Nightly);
+    assert_eq!(version.host, "x86_64-unknown-linux-gnu");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.5.0-nightly (65d5c0833 2015-09-29)"
+    );
+    assert_eq!(version.llvm_version, None);
+}
+
+#[test]
+fn parse_stable() {
+    let version = version_meta_for(
+        "rustc 1.3.0 (9a92aaf19 2015-09-15)
+binary: rustc
+commit-hash: 9a92aaf19a64603b02b4130fe52958cc12488900
+commit-date: 2015-09-15
+host: x86_64-unknown-linux-gnu
+release: 1.3.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.3.0").unwrap());
+    assert_eq!(
+        version.commit_hash,
+        Some("9a92aaf19a64603b02b4130fe52958cc12488900".into())
+    );
+    assert_eq!(version.commit_date, Some("2015-09-15".into()));
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "x86_64-unknown-linux-gnu");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.3.0 (9a92aaf19 2015-09-15)"
+    );
+    assert_eq!(version.llvm_version, None);
+}
+
+#[test]
+fn parse_1_16_0_nightly() {
+    let version = version_meta_for(
+        "rustc 1.16.0-nightly (5d994d8b7 2017-01-05)
+binary: rustc
+commit-hash: 5d994d8b7e482e87467d4a521911477bd8284ce3
+commit-date: 2017-01-05
+host: x86_64-unknown-linux-gnu
+release: 1.16.0-nightly
+LLVM version: 3.9",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.16.0-nightly").unwrap());
+    assert_eq!(
+        version.commit_hash,
+        Some("5d994d8b7e482e87467d4a521911477bd8284ce3".into())
+    );
+    assert_eq!(version.commit_date, Some("2017-01-05".into()));
+    assert_eq!(version.channel, Channel::Nightly);
+    assert_eq!(version.host, "x86_64-unknown-linux-gnu");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.16.0-nightly (5d994d8b7 2017-01-05)"
+    );
+    assert_eq!(
+        version.llvm_version,
+        Some(LlvmVersion { major: 3, minor: 9 })
+    );
+}
+
+#[test]
+fn parse_1_47_0_stable() {
+    let version = version_meta_for(
+        "rustc 1.47.0 (18bf6b4f0 2020-10-07)
+binary: rustc
+commit-hash: 18bf6b4f01a6feaf7259ba7cdae58031af1b7b39
+commit-date: 2020-10-07
+host: powerpc64le-unknown-linux-gnu
+release: 1.47.0
+LLVM version: 11.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.47.0").unwrap());
+    assert_eq!(
+        version.commit_hash,
+        Some("18bf6b4f01a6feaf7259ba7cdae58031af1b7b39".into())
+    );
+    assert_eq!(version.commit_date, Some("2020-10-07".into()));
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "powerpc64le-unknown-linux-gnu");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.47.0 (18bf6b4f0 2020-10-07)"
+    );
+    assert_eq!(
+        version.llvm_version,
+        Some(LlvmVersion {
+            major: 11,
+            minor: 0,
+        })
+    );
+}
+
+#[test]
+fn parse_llvm_micro() {
+    let version = version_meta_for(
+        "rustc 1.51.0-nightly (4253153db 2021-01-17)
+binary: rustc
+commit-hash: 4253153db205251f72ea4493687a31e04a2a8ca0
+commit-date: 2021-01-17
+host: x86_64-pc-windows-msvc
+release: 1.51.0-nightly
+LLVM version: 11.0.1",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.51.0-nightly").unwrap());
+    assert_eq!(
+        version.commit_hash.unwrap(),
+        "4253153db205251f72ea4493687a31e04a2a8ca0"
+    );
+    assert_eq!(version.commit_date.unwrap(), "2021-01-17");
+    assert_eq!(version.host, "x86_64-pc-windows-msvc");
+    assert_eq!(
+        version.short_version_string,
+        "rustc 1.51.0-nightly (4253153db 2021-01-17)"
+    );
+    assert_eq!(
+        version.llvm_version,
+        Some(LlvmVersion {
+            major: 11,
+            minor: 0
+        })
+    );
+}
+
+#[test]
+fn parse_debian_buster() {
+    let version = version_meta_for(
+        "rustc 1.41.1
+binary: rustc
+commit-hash: unknown
+commit-date: unknown
+host: powerpc64le-unknown-linux-gnu
+release: 1.41.1
+LLVM version: 7.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.41.1").unwrap());
+    assert_eq!(version.commit_hash, None);
+    assert_eq!(version.commit_date, None);
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "powerpc64le-unknown-linux-gnu");
+    assert_eq!(version.short_version_string, "rustc 1.41.1");
+    assert_eq!(
+        version.llvm_version,
+        Some(LlvmVersion { major: 7, minor: 0 })
+    );
+}
+
+#[test]
+fn parse_termux() {
+    let version = version_meta_for(
+        "rustc 1.46.0
+binary: rustc
+commit-hash: unknown
+commit-date: unknown
+host: aarch64-linux-android
+release: 1.46.0
+LLVM version: 10.0",
+    )
+    .unwrap();
+
+    assert_eq!(version.semver, Version::parse("1.46.0").unwrap());
+    assert_eq!(version.commit_hash, None);
+    assert_eq!(version.commit_date, None);
+    assert_eq!(version.channel, Channel::Stable);
+    assert_eq!(version.host, "aarch64-linux-android");
+    assert_eq!(version.short_version_string, "rustc 1.46.0");
+    assert_eq!(
+        version.llvm_version,
+        Some(LlvmVersion {
+            major: 10,
+            minor: 0,
+        })
+    );
+}
+
+#[test]
+fn parse_llvm_version_empty() {
+    let res: Result<LlvmVersion, _> = "".parse();
+    assert!(match res {
+        Err(LlvmVersionParseError::ParseIntError(_)) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_invalid_char() {
+    let res: Result<LlvmVersion, _> = "A".parse();
+    assert!(match res {
+        Err(LlvmVersionParseError::ParseIntError(_)) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_overflow() {
+    let res: Result<LlvmVersion, _> = "9999999999999999999999999999999".parse();
+    assert!(match res {
+        Err(LlvmVersionParseError::ParseIntError(_)) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_leading_zero_on_zero() {
+    let res: Result<LlvmVersion, _> = "00".parse();
+    assert!(match res {
+        Err(LlvmVersionParseError::ComponentMustNotHaveLeadingZeros) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_leading_zero_on_nonzero() {
+    let res: Result<LlvmVersion, _> = "01".parse();
+    assert!(match res {
+        Err(LlvmVersionParseError::ComponentMustNotHaveLeadingZeros) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_4_components() {
+    let res: Result<LlvmVersion, _> = "4.0.0.0".parse();
+
+    assert!(match res {
+        Err(LlvmVersionParseError::TooManyComponents) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_component_sign_plus() {
+    let res: Result<LlvmVersion, _> = "1.+3".parse();
+
+    assert!(match res {
+        Err(LlvmVersionParseError::ComponentMustNotHaveSign) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_component_sign_minus() {
+    let res: Result<LlvmVersion, _> = "1.-3".parse();
+
+    assert!(match res {
+        Err(LlvmVersionParseError::ComponentMustNotHaveSign) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_3() {
+    let res: Result<LlvmVersion, _> = "3".parse();
+
+    assert!(match res {
+        Err(LlvmVersionParseError::MinorVersionRequiredBefore4) => true,
+        _ => false,
+    });
+}
+
+#[test]
+fn parse_llvm_version_5() {
+    let v: LlvmVersion = "5".parse().unwrap();
+    assert_eq!(v, LlvmVersion { major: 5, minor: 0 });
+}
+
+#[test]
+fn parse_llvm_version_5_0() {
+    let v: LlvmVersion = "5.0".parse().unwrap();
+    assert_eq!(v, LlvmVersion { major: 5, minor: 0 });
+}
+
+#[test]
+fn parse_llvm_version_4_0() {
+    let v: LlvmVersion = "4.0".parse().unwrap();
+    assert_eq!(v, LlvmVersion { major: 4, minor: 0 });
+}
+
+#[test]
+fn parse_llvm_version_3_0() {
+    let v: LlvmVersion = "3.0".parse().unwrap();
+    assert_eq!(v, LlvmVersion { major: 3, minor: 0 });
+}
+
+#[test]
+fn parse_llvm_version_3_9() {
+    let v: LlvmVersion = "3.9".parse().unwrap();
+    assert_eq!(v, LlvmVersion { major: 3, minor: 9 });
+}
+
+#[test]
+fn parse_llvm_version_11_0() {
+    let v: LlvmVersion = "11.0".parse().unwrap();
+    assert_eq!(
+        v,
+        LlvmVersion {
+            major: 11,
+            minor: 0
+        }
+    );
+}
+
+#[test]
+fn parse_llvm_version_11() {
+    let v: LlvmVersion = "11".parse().unwrap();
+    assert_eq!(
+        v,
+        LlvmVersion {
+            major: 11,
+            minor: 0
+        }
+    );
+}
+
+#[test]
+fn test_llvm_version_comparison() {
+    // check that field order is correct
+    assert!(LlvmVersion { major: 3, minor: 9 } < LlvmVersion { major: 4, minor: 0 });
+}
+
+/*
+#[test]
+fn version_matches_replacement() {
+    let f = |s1: &str, s2: &str| {
+        let a = Version::parse(s1).unwrap();
+        let b = Version::parse(s2).unwrap();
+        println!("{} <= {} : {}", s1, s2, a <= b);
+    };
+
+    println!();
+
+    f("1.5.0",         "1.5.0");
+    f("1.5.0-nightly", "1.5.0");
+    f("1.5.0",         "1.5.0-nightly");
+    f("1.5.0-nightly", "1.5.0-nightly");
+
+    f("1.5.0",         "1.6.0");
+    f("1.5.0-nightly", "1.6.0");
+    f("1.5.0",         "1.6.0-nightly");
+    f("1.5.0-nightly", "1.6.0-nightly");
+
+    panic!();
+
+}
+*/
diff --git a/third_party/rust/semver/v1/BUILD.gn b/third_party/rust/semver/v1/BUILD.gn
new file mode 100644
index 0000000..3d5f002
--- /dev/null
+++ b/third_party/rust/semver/v1/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2022 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("//build/rust/cargo_crate.gni")
+
+cargo_crate("lib") {
+  crate_name = "semver"
+  epoch = "1"
+  crate_type = "rlib"
+
+  # Only for usage from third-party crates. Add the crate to
+  # third_party.toml to use it from first-party code.
+  visibility = [ "//third_party/rust/*" ]
+  crate_root = "crate/src/lib.rs"
+
+  # Unit tests skipped. Generate with --with-tests to include them.
+  skip_unit_tests = true
+  sources = [ "crate/src/lib.rs" ]
+  edition = "2018"
+  features = [ "std" ]
+  build_root = "crate/build.rs"
+  build_sources = [ "crate/build.rs" ]
+}
diff --git a/third_party/rust/semver/v1/README.chromium b/third_party/rust/semver/v1/README.chromium
new file mode 100644
index 0000000..0e29762
--- /dev/null
+++ b/third_party/rust/semver/v1/README.chromium
@@ -0,0 +1,6 @@
+Name: semver
+URL: https://crates.io/crates/semver
+Description: Parser and evaluator for Cargo's flavor of Semantic Versioning
+Version: 1.0.4
+Security Critical: no
+License: Apache 2.0
diff --git a/third_party/rust/semver/v1/crate/.cargo_vcs_info.json b/third_party/rust/semver/v1/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..5655b89f
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "ea9ea80c023ba3913b9ab0af1d983f137b4110a5"
+  }
+}
diff --git a/third_party/rust/semver/v1/crate/.clippy.toml b/third_party/rust/semver/v1/crate/.clippy.toml
new file mode 100644
index 0000000..3d30690
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/.clippy.toml
@@ -0,0 +1 @@
+msrv = "1.31.0"
diff --git a/third_party/rust/semver/v1/crate/.github/workflows/ci.yml b/third_party/rust/semver/v1/crate/.github/workflows/ci.yml
new file mode 100644
index 0000000..267b7aeb
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/.github/workflows/ci.yml
@@ -0,0 +1,53 @@
+name: CI
+
+on:
+  push:
+  pull_request:
+  schedule: [cron: "40 1 * * *"]
+
+jobs:
+  test:
+    name: Rust ${{matrix.rust}}
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        rust: [nightly, beta, stable, 1.52.0, 1.46.0, 1.45.0, 1.40.0, 1.39.0, 1.36.0, 1.33.0, 1.32.0, 1.31.0]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+      - run: cargo test
+      - run: cargo check --no-default-features
+      - run: cargo check --features serde
+      - run: cargo check --no-default-features --features serde
+
+  node:
+    name: Node
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+      - run: npm install semver
+      - run: cargo test
+        env:
+          RUSTFLAGS: --cfg test_node_semver
+
+  clippy:
+    name: Clippy
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@clippy
+      - run: cargo clippy -- -Dclippy::all -Dclippy::pedantic
+
+  miri:
+    name: Miri
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@nightly
+        with:
+          components: miri
+      - run: cargo miri test
diff --git a/third_party/rust/semver/v1/crate/.gitignore b/third_party/rust/semver/v1/crate/.gitignore
new file mode 100644
index 0000000..744c1ef
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/.gitignore
@@ -0,0 +1,5 @@
+/node_modules
+/package-lock.json
+/package.json
+/target
+Cargo.lock
diff --git a/third_party/rust/semver/v1/crate/Cargo.toml b/third_party/rust/semver/v1/crate/Cargo.toml
new file mode 100644
index 0000000..fb868d7
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/Cargo.toml
@@ -0,0 +1,32 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "semver"
+version = "1.0.4"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Parser and evaluator for Cargo's flavor of Semantic Versioning"
+documentation = "https://docs.rs/semver"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/semver"
+[package.metadata.docs.rs]
+rustdoc-args = ["--cfg", "doc_cfg"]
+targets = ["x86_64-unknown-linux-gnu"]
+[dependencies.serde]
+version = "1.0"
+optional = true
+default-features = false
+
+[features]
+default = ["std"]
+std = []
diff --git a/third_party/rust/semver/v1/crate/Cargo.toml.orig b/third_party/rust/semver/v1/crate/Cargo.toml.orig
new file mode 100644
index 0000000..71e9895
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/Cargo.toml.orig
@@ -0,0 +1,21 @@
+[package]
+name = "semver"
+version = "1.0.4"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+description = "Parser and evaluator for Cargo's flavor of Semantic Versioning"
+repository = "https://github.com/dtolnay/semver"
+documentation = "https://docs.rs/semver"
+readme = "README.md"
+
+[features]
+default = ["std"]
+std = []
+
+[dependencies]
+serde = { version = "1.0", optional = true, default-features = false }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+rustdoc-args = ["--cfg", "doc_cfg"]
diff --git a/third_party/rust/semver/v1/crate/LICENSE-APACHE b/third_party/rust/semver/v1/crate/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/rust/semver/v1/crate/LICENSE-MIT b/third_party/rust/semver/v1/crate/LICENSE-MIT
new file mode 100644
index 0000000..31aa7938
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/semver/v1/crate/README.md b/third_party/rust/semver/v1/crate/README.md
new file mode 100644
index 0000000..109efa3
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/README.md
@@ -0,0 +1,84 @@
+semver
+======
+
+[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/semver-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/semver)
+[<img alt="crates.io" src="https://img.shields.io/crates/v/semver.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/semver)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-semver-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/semver/1.0.0)
+[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/semver/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/semver/actions?query=branch%3Amaster)
+
+A parser and evaluator for Cargo's flavor of Semantic Versioning.
+
+Semantic Versioning (see <https://semver.org>) is a guideline for how version
+numbers are assigned and incremented. It is widely followed within the
+Cargo/crates.io ecosystem for Rust.
+
+```toml
+[dependencies]
+semver = "1.0"
+```
+
+*Compiler support: requires rustc 1.31+*
+
+<br>
+
+## Example
+
+```rust
+use semver::{BuildMetadata, Prerelease, Version, VersionReq};
+
+fn main() {
+    let req = VersionReq::parse(">=1.2.3, <1.8.0").unwrap();
+
+    // Check whether this requirement matches version 1.2.3-alpha.1 (no)
+    let version = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: Prerelease::new("alpha.1").unwrap(),
+        build: BuildMetadata::EMPTY,
+    };
+    assert!(!req.matches(&version));
+
+    // Check whether it matches 1.3.0 (yes it does)
+    let version = Version::parse("1.3.0").unwrap();
+    assert!(req.matches(&version));
+}
+```
+
+<br>
+
+## Scope of this crate
+
+Besides Cargo, several other package ecosystems and package managers for other
+languages also use SemVer:&ensp;RubyGems/Bundler for Ruby, npm for JavaScript,
+Composer for PHP, CocoaPods for Objective-C...
+
+The `semver` crate is specifically intended to implement Cargo's interpretation
+of Semantic Versioning.
+
+Where the various tools differ in their interpretation or implementation of the
+spec, this crate follows the implementation choices made by Cargo. If you are
+operating on version numbers from some other package ecosystem, you will want to
+use a different semver library which is appropriate to that ecosystem.
+
+The extent of Cargo's SemVer support is documented in the *[Specifying
+Dependencies]* chapter of the Cargo reference.
+
+[Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/third_party/rust/semver/v1/crate/benches/parse.rs b/third_party/rust/semver/v1/crate/benches/parse.rs
new file mode 100644
index 0000000..d6aded7
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/benches/parse.rs
@@ -0,0 +1,24 @@
+#![feature(test)]
+
+extern crate test;
+
+use semver::{Prerelease, Version, VersionReq};
+use test::{black_box, Bencher};
+
+#[bench]
+fn parse_prerelease(b: &mut Bencher) {
+    let text = "x.7.z.92";
+    b.iter(|| black_box(text).parse::<Prerelease>().unwrap());
+}
+
+#[bench]
+fn parse_version(b: &mut Bencher) {
+    let text = "1.0.2021-beta+exp.sha.5114f85";
+    b.iter(|| black_box(text).parse::<Version>().unwrap());
+}
+
+#[bench]
+fn parse_version_req(b: &mut Bencher) {
+    let text = ">=1.2.3, <2.0.0";
+    b.iter(|| black_box(text).parse::<VersionReq>().unwrap());
+}
diff --git a/third_party/rust/semver/v1/crate/build.rs b/third_party/rust/semver/v1/crate/build.rs
new file mode 100644
index 0000000..b39468d2
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/build.rs
@@ -0,0 +1,79 @@
+use std::env;
+use std::process::Command;
+use std::str;
+
+fn main() {
+    let compiler = match rustc_minor_version() {
+        Some(compiler) => compiler,
+        None => return,
+    };
+
+    if compiler < 32 {
+        // u64::from_ne_bytes.
+        // https://doc.rust-lang.org/std/primitive.u64.html#method.from_ne_bytes
+        println!("cargo:rustc-cfg=no_from_ne_bytes");
+    }
+
+    if compiler < 33 {
+        // Exhaustive integer patterns. On older compilers, a final `_` arm is
+        // required even if every possible integer value is otherwise covered.
+        // https://github.com/rust-lang/rust/issues/50907
+        println!("cargo:rustc-cfg=no_exhaustive_int_match");
+    }
+
+    if compiler < 36 {
+        // extern crate alloc.
+        // https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html#the-alloc-crate-is-stable
+        println!("cargo:rustc-cfg=no_alloc_crate");
+    }
+
+    if compiler < 39 {
+        // const Vec::new.
+        // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new
+        println!("cargo:rustc-cfg=no_const_vec_new");
+    }
+
+    if compiler < 40 {
+        // #[non_exhaustive].
+        // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#non_exhaustive-structs-enums-and-variants
+        println!("cargo:rustc-cfg=no_non_exhaustive");
+    }
+
+    if compiler < 45 {
+        // String::strip_prefix.
+        // https://doc.rust-lang.org/std/primitive.str.html#method.repeat
+        println!("cargo:rustc-cfg=no_str_strip_prefix");
+    }
+
+    if compiler < 46 {
+        // #[track_caller].
+        // https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html#track_caller
+        println!("cargo:rustc-cfg=no_track_caller");
+    }
+
+    if compiler < 52 {
+        // #![deny(unsafe_op_in_unsafe_fn)].
+        // https://github.com/rust-lang/rust/issues/71668
+        println!("cargo:rustc-cfg=no_unsafe_op_in_unsafe_fn_lint");
+    }
+
+    if compiler < 53 {
+        // Efficient intrinsics for count-leading-zeros and count-trailing-zeros
+        // on NonZero integers stabilized in 1.53.0. On many architectures these
+        // are more efficient than counting zeros on ordinary zeroable integers.
+        // https://doc.rust-lang.org/std/num/struct.NonZeroU64.html#method.leading_zeros
+        // https://doc.rust-lang.org/std/num/struct.NonZeroU64.html#method.trailing_zeros
+        println!("cargo:rustc-cfg=no_nonzero_bitscan");
+    }
+}
+
+fn rustc_minor_version() -> Option<u32> {
+    let rustc = env::var_os("RUSTC")?;
+    let output = Command::new(rustc).arg("--version").output().ok()?;
+    let version = str::from_utf8(&output.stdout).ok()?;
+    let mut pieces = version.split('.');
+    if pieces.next() != Some("rustc 1") {
+        return None;
+    }
+    pieces.next()?.parse().ok()
+}
diff --git a/third_party/rust/semver/v1/crate/src/backport.rs b/third_party/rust/semver/v1/crate/src/backport.rs
new file mode 100644
index 0000000..f3a69c95
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/backport.rs
@@ -0,0 +1,63 @@
+#[cfg(no_str_strip_prefix)] // rustc <1.45
+pub(crate) trait StripPrefixExt {
+    fn strip_prefix(&self, ch: char) -> Option<&str>;
+}
+
+#[cfg(no_str_strip_prefix)]
+impl StripPrefixExt for str {
+    fn strip_prefix(&self, ch: char) -> Option<&str> {
+        if self.starts_with(ch) {
+            Some(&self[ch.len_utf8()..])
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(no_from_ne_bytes)] // rustc <1.32
+pub(crate) trait FromNeBytes {
+    fn from_ne_bytes(bytes: [u8; 8]) -> Self;
+}
+
+#[cfg(no_from_ne_bytes)]
+impl FromNeBytes for u64 {
+    fn from_ne_bytes(bytes: [u8; 8]) -> Self {
+        unsafe { std::mem::transmute(bytes) }
+    }
+}
+
+pub(crate) use crate::alloc::vec::Vec;
+
+#[cfg(no_alloc_crate)] // rustc <1.36
+pub(crate) mod alloc {
+    pub use std::vec;
+
+    pub mod alloc {
+        use std::mem;
+
+        pub struct Layout {
+            size: usize,
+        }
+
+        impl Layout {
+            pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
+                assert_eq!(align, 2);
+                Layout { size }
+            }
+        }
+
+        pub unsafe fn alloc(layout: Layout) -> *mut u8 {
+            let len_u16 = (layout.size + 1) / 2;
+            let mut vec = Vec::new();
+            vec.reserve_exact(len_u16);
+            let ptr: *mut u16 = vec.as_mut_ptr();
+            mem::forget(vec);
+            ptr as *mut u8
+        }
+
+        pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) {
+            let len_u16 = (layout.size + 1) / 2;
+            unsafe { Vec::from_raw_parts(ptr as *mut u16, 0, len_u16) };
+        }
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/src/display.rs b/third_party/rust/semver/v1/crate/src/display.rs
new file mode 100644
index 0000000..3c2871bb
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/display.rs
@@ -0,0 +1,165 @@
+use crate::{BuildMetadata, Comparator, Op, Prerelease, Version, VersionReq};
+use core::fmt::{self, Alignment, Debug, Display, Write};
+
+impl Display for Version {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let do_display = |formatter: &mut fmt::Formatter| -> fmt::Result {
+            write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)?;
+            if !self.pre.is_empty() {
+                write!(formatter, "-{}", self.pre)?;
+            }
+            if !self.build.is_empty() {
+                write!(formatter, "+{}", self.build)?;
+            }
+            Ok(())
+        };
+
+        let do_len = || -> usize {
+            digits(self.major)
+                + 1
+                + digits(self.minor)
+                + 1
+                + digits(self.patch)
+                + !self.pre.is_empty() as usize
+                + self.pre.len()
+                + !self.build.is_empty() as usize
+                + self.build.len()
+        };
+
+        pad(formatter, do_display, do_len)
+    }
+}
+
+impl Display for VersionReq {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        if self.comparators.is_empty() {
+            return formatter.write_str("*");
+        }
+        for (i, comparator) in self.comparators.iter().enumerate() {
+            if i > 0 {
+                formatter.write_str(", ")?;
+            }
+            write!(formatter, "{}", comparator)?;
+        }
+        Ok(())
+    }
+}
+
+impl Display for Comparator {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let op = match self.op {
+            Op::Exact => "=",
+            Op::Greater => ">",
+            Op::GreaterEq => ">=",
+            Op::Less => "<",
+            Op::LessEq => "<=",
+            Op::Tilde => "~",
+            Op::Caret => "^",
+            Op::Wildcard => "",
+            #[cfg(no_non_exhaustive)]
+            Op::__NonExhaustive => unreachable!(),
+        };
+        formatter.write_str(op)?;
+        write!(formatter, "{}", self.major)?;
+        if let Some(minor) = &self.minor {
+            write!(formatter, ".{}", minor)?;
+            if let Some(patch) = &self.patch {
+                write!(formatter, ".{}", patch)?;
+                if !self.pre.is_empty() {
+                    write!(formatter, "-{}", self.pre)?;
+                }
+            } else if self.op == Op::Wildcard {
+                formatter.write_str(".*")?;
+            }
+        } else if self.op == Op::Wildcard {
+            formatter.write_str(".*")?;
+        }
+        Ok(())
+    }
+}
+
+impl Display for Prerelease {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(self.as_str())
+    }
+}
+
+impl Display for BuildMetadata {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(self.as_str())
+    }
+}
+
+impl Debug for Version {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let mut debug = formatter.debug_struct("Version");
+        debug
+            .field("major", &self.major)
+            .field("minor", &self.minor)
+            .field("patch", &self.patch);
+        if !self.pre.is_empty() {
+            debug.field("pre", &self.pre);
+        }
+        if !self.build.is_empty() {
+            debug.field("build", &self.build);
+        }
+        debug.finish()
+    }
+}
+
+impl Debug for Prerelease {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        write!(formatter, "Prerelease(\"{}\")", self)
+    }
+}
+
+impl Debug for BuildMetadata {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        write!(formatter, "BuildMetadata(\"{}\")", self)
+    }
+}
+
+fn pad(
+    formatter: &mut fmt::Formatter,
+    do_display: impl FnOnce(&mut fmt::Formatter) -> fmt::Result,
+    do_len: impl FnOnce() -> usize,
+) -> fmt::Result {
+    let min_width = match formatter.width() {
+        Some(min_width) => min_width,
+        None => return do_display(formatter),
+    };
+
+    let len = do_len();
+    if len >= min_width {
+        return do_display(formatter);
+    }
+
+    let default_align = Alignment::Left;
+    let align = formatter.align().unwrap_or(default_align);
+    let padding = min_width - len;
+    let (pre_pad, post_pad) = match align {
+        Alignment::Left => (0, padding),
+        Alignment::Right => (padding, 0),
+        Alignment::Center => (padding / 2, (padding + 1) / 2),
+    };
+
+    let fill = formatter.fill();
+    for _ in 0..pre_pad {
+        formatter.write_char(fill)?;
+    }
+
+    do_display(formatter)?;
+
+    for _ in 0..post_pad {
+        formatter.write_char(fill)?;
+    }
+    Ok(())
+}
+
+fn digits(val: u64) -> usize {
+    if val < 10 {
+        1
+    } else {
+        1 + digits(val / 10)
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/src/error.rs b/third_party/rust/semver/v1/crate/src/error.rs
new file mode 100644
index 0000000..4d57950
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/error.rs
@@ -0,0 +1,90 @@
+use crate::parse::Error;
+use core::fmt::{self, Debug, Display};
+
+pub(crate) enum ErrorKind {
+    UnexpectedEnd(Position),
+    UnexpectedChar(Position, char),
+    UnexpectedCharAfter(Position, char),
+    ExpectedCommaFound(Position, char),
+    LeadingZero(Position),
+    Overflow(Position),
+    EmptySegment(Position),
+    IllegalCharacter(Position),
+    UnexpectedAfterWildcard,
+    ExcessiveComparators,
+}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub(crate) enum Position {
+    Major,
+    Minor,
+    Patch,
+    Pre,
+    Build,
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
+impl std::error::Error for Error {}
+
+impl Display for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match &self.kind {
+            ErrorKind::UnexpectedEnd(pos) => {
+                write!(formatter, "unexpected end of input while parsing {}", pos)
+            }
+            ErrorKind::UnexpectedChar(pos, ch) => {
+                write!(
+                    formatter,
+                    "unexpected character {:?} while parsing {}",
+                    ch, pos,
+                )
+            }
+            ErrorKind::UnexpectedCharAfter(pos, ch) => {
+                write!(formatter, "unexpected character {:?} after {}", ch, pos)
+            }
+            ErrorKind::ExpectedCommaFound(pos, ch) => {
+                write!(formatter, "expected comma after {}, found {:?}", pos, ch)
+            }
+            ErrorKind::LeadingZero(pos) => {
+                write!(formatter, "invalid leading zero in {}", pos)
+            }
+            ErrorKind::Overflow(pos) => {
+                write!(formatter, "value of {} exceeds u64::MAX", pos)
+            }
+            ErrorKind::EmptySegment(pos) => {
+                write!(formatter, "empty identifier segment in {}", pos)
+            }
+            ErrorKind::IllegalCharacter(pos) => {
+                write!(formatter, "unexpected character in {}", pos)
+            }
+            ErrorKind::UnexpectedAfterWildcard => {
+                formatter.write_str("unexpected character after wildcard in version req")
+            }
+            ErrorKind::ExcessiveComparators => {
+                formatter.write_str("excessive number of version comparators")
+            }
+        }
+    }
+}
+
+impl Display for Position {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(match self {
+            Position::Major => "major version number",
+            Position::Minor => "minor version number",
+            Position::Patch => "patch version number",
+            Position::Pre => "pre-release identifier",
+            Position::Build => "build metadata",
+        })
+    }
+}
+
+impl Debug for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("Error(\"")?;
+        Display::fmt(self, formatter)?;
+        formatter.write_str("\")")?;
+        Ok(())
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/src/eval.rs b/third_party/rust/semver/v1/crate/src/eval.rs
new file mode 100644
index 0000000..e6e38949
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/eval.rs
@@ -0,0 +1,181 @@
+use crate::{Comparator, Op, Version, VersionReq};
+
+pub(crate) fn matches_req(req: &VersionReq, ver: &Version) -> bool {
+    for cmp in &req.comparators {
+        if !matches_impl(cmp, ver) {
+            return false;
+        }
+    }
+
+    if ver.pre.is_empty() {
+        return true;
+    }
+
+    // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it
+    // will only be allowed to satisfy req if at least one comparator with the
+    // same major.minor.patch also has a prerelease tag.
+    for cmp in &req.comparators {
+        if pre_is_compatible(cmp, ver) {
+            return true;
+        }
+    }
+
+    false
+}
+
+pub(crate) fn matches_comparator(cmp: &Comparator, ver: &Version) -> bool {
+    matches_impl(cmp, ver) && (ver.pre.is_empty() || pre_is_compatible(cmp, ver))
+}
+
+fn matches_impl(cmp: &Comparator, ver: &Version) -> bool {
+    match cmp.op {
+        Op::Exact | Op::Wildcard => matches_exact(cmp, ver),
+        Op::Greater => matches_greater(cmp, ver),
+        Op::GreaterEq => matches_exact(cmp, ver) || matches_greater(cmp, ver),
+        Op::Less => matches_less(cmp, ver),
+        Op::LessEq => matches_exact(cmp, ver) || matches_less(cmp, ver),
+        Op::Tilde => matches_tilde(cmp, ver),
+        Op::Caret => matches_caret(cmp, ver),
+        #[cfg(no_non_exhaustive)]
+        Op::__NonExhaustive => unreachable!(),
+    }
+}
+
+fn matches_exact(cmp: &Comparator, ver: &Version) -> bool {
+    if ver.major != cmp.major {
+        return false;
+    }
+
+    if let Some(minor) = cmp.minor {
+        if ver.minor != minor {
+            return false;
+        }
+    }
+
+    if let Some(patch) = cmp.patch {
+        if ver.patch != patch {
+            return false;
+        }
+    }
+
+    ver.pre == cmp.pre
+}
+
+fn matches_greater(cmp: &Comparator, ver: &Version) -> bool {
+    if ver.major != cmp.major {
+        return ver.major > cmp.major;
+    }
+
+    match cmp.minor {
+        None => return false,
+        Some(minor) => {
+            if ver.minor != minor {
+                return ver.minor > minor;
+            }
+        }
+    }
+
+    match cmp.patch {
+        None => return false,
+        Some(patch) => {
+            if ver.patch != patch {
+                return ver.patch > patch;
+            }
+        }
+    }
+
+    ver.pre > cmp.pre
+}
+
+fn matches_less(cmp: &Comparator, ver: &Version) -> bool {
+    if ver.major != cmp.major {
+        return ver.major < cmp.major;
+    }
+
+    match cmp.minor {
+        None => return false,
+        Some(minor) => {
+            if ver.minor != minor {
+                return ver.minor < minor;
+            }
+        }
+    }
+
+    match cmp.patch {
+        None => return false,
+        Some(patch) => {
+            if ver.patch != patch {
+                return ver.patch < patch;
+            }
+        }
+    }
+
+    ver.pre < cmp.pre
+}
+
+fn matches_tilde(cmp: &Comparator, ver: &Version) -> bool {
+    if ver.major != cmp.major {
+        return false;
+    }
+
+    if let Some(minor) = cmp.minor {
+        if ver.minor != minor {
+            return false;
+        }
+    }
+
+    if let Some(patch) = cmp.patch {
+        if ver.patch != patch {
+            return ver.patch > patch;
+        }
+    }
+
+    ver.pre >= cmp.pre
+}
+
+fn matches_caret(cmp: &Comparator, ver: &Version) -> bool {
+    if ver.major != cmp.major {
+        return false;
+    }
+
+    let minor = match cmp.minor {
+        None => return true,
+        Some(minor) => minor,
+    };
+
+    let patch = match cmp.patch {
+        None => {
+            if cmp.major > 0 {
+                return ver.minor >= minor;
+            } else {
+                return ver.minor == minor;
+            }
+        }
+        Some(patch) => patch,
+    };
+
+    if cmp.major > 0 {
+        if ver.minor != minor {
+            return ver.minor > minor;
+        } else if ver.patch != patch {
+            return ver.patch > patch;
+        }
+    } else if minor > 0 {
+        if ver.minor != minor {
+            return false;
+        } else if ver.patch != patch {
+            return ver.patch > patch;
+        }
+    } else if ver.minor != minor || ver.patch != patch {
+        return false;
+    }
+
+    ver.pre >= cmp.pre
+}
+
+fn pre_is_compatible(cmp: &Comparator, ver: &Version) -> bool {
+    cmp.major == ver.major
+        && cmp.minor == Some(ver.minor)
+        && cmp.patch == Some(ver.patch)
+        && !cmp.pre.is_empty()
+}
diff --git a/third_party/rust/semver/v1/crate/src/identifier.rs b/third_party/rust/semver/v1/crate/src/identifier.rs
new file mode 100644
index 0000000..500239e
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/identifier.rs
@@ -0,0 +1,356 @@
+// This module implements Identifier, a short-optimized string allowed to
+// contain only the ASCII characters hyphen, dot, 0-9, A-Z, a-z.
+//
+// As of mid-2021, the distribution of pre-release lengths on crates.io is:
+//
+//     length  count         length  count         length  count
+//        0  355929            11      81            24       2
+//        1     208            12      48            25       6
+//        2     236            13      55            26      10
+//        3    1909            14      25            27       4
+//        4    1284            15      15            28       1
+//        5    1742            16      35            30       1
+//        6    3440            17       9            31       5
+//        7    5624            18       6            32       1
+//        8    1321            19      12            36       2
+//        9     179            20       2            37     379
+//       10      65            23      11
+//
+// and the distribution of build metadata lengths is:
+//
+//     length  count         length  count         length  count
+//        0  364445             8    7725            18       1
+//        1      72             9      16            19       1
+//        2       7            10      85            20       1
+//        3      28            11      17            22       4
+//        4       9            12      10            26       1
+//        5      68            13       9            27       1
+//        6      73            14      10            40       5
+//        7      53            15       6
+//
+// Therefore it really behooves us to be able to use the entire 8 bytes of a
+// pointer for inline storage. For both pre-release and build metadata there are
+// vastly more strings with length exactly 8 bytes than the sum over all lengths
+// longer than 8 bytes.
+//
+// To differentiate the inline representation from the heap allocated long
+// representation, we'll allocate heap pointers with 2-byte alignment so that
+// they are guaranteed to have an unset least significant bit. Then in the repr
+// we store for pointers, we rotate a 1 into the most significant bit of the
+// most significant byte, which is never set for an ASCII byte.
+//
+// Inline repr:
+//
+//     0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx
+//
+// Heap allocated repr:
+//
+//     1ppppppp pppppppp pppppppp pppppppp pppppppp pppppppp pppppppp pppppppp 0
+//     ^ most significant bit   least significant bit of orig ptr, rotated out ^
+//
+// Since the most significant bit doubles as a sign bit for the similarly sized
+// signed integer type, the CPU has an efficient instruction for inspecting it,
+// meaning we can differentiate between an inline repr and a heap allocated repr
+// in one instruction. Effectively an inline repr always looks like a positive
+// i64 while a heap allocated repr always looks like a negative i64.
+//
+// For the inline repr, we store \0 padding on the end of the stored characters,
+// and thus the string length is readily determined efficiently by a cttz (count
+// trailing zeros) or bsf (bit scan forward) instruction.
+//
+// For the heap allocated repr, the length is encoded as a base-128 varint at
+// the head of the allocation.
+//
+// Empty strings are stored as an all-1 bit pattern, corresponding to -1i64.
+// Consequently the all-0 bit pattern is never a legal representation in any
+// repr, leaving it available as a niche for downstream code. For example this
+// allows size_of::<Version>() == size_of::<Option<Version>>().
+
+use crate::alloc::alloc::{alloc, dealloc, Layout};
+#[cfg(no_from_ne_bytes)]
+use crate::backport::FromNeBytes;
+use core::mem;
+use core::num::{NonZeroU64, NonZeroUsize};
+use core::ptr;
+use core::slice;
+use core::str;
+
+#[repr(transparent)]
+pub(crate) struct Identifier {
+    repr: NonZeroU64,
+}
+
+impl Identifier {
+    const EMPTY: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(!0) };
+
+    pub(crate) const fn empty() -> Self {
+        // `mov rax, -1`
+        Identifier { repr: Self::EMPTY }
+    }
+
+    // SAFETY: string must be ASCII and not contain \0 bytes.
+    pub(crate) unsafe fn new_unchecked(string: &str) -> Self {
+        let len = string.len();
+        let repr = match len as u64 {
+            0 => Self::EMPTY,
+            1..=8 => {
+                let mut bytes = [0u8; 8];
+                // SAFETY: string is big enough to read len bytes, bytes is big
+                // enough to write len bytes, and they do not overlap.
+                unsafe { ptr::copy_nonoverlapping(string.as_ptr(), bytes.as_mut_ptr(), len) };
+                // SAFETY: it's nonzero because the input string was at least 1
+                // byte of ASCII and did not contain \0.
+                unsafe { NonZeroU64::new_unchecked(u64::from_ne_bytes(bytes)) }
+            }
+            9..=0xff_ffff_ffff_ffff => {
+                // SAFETY: len is in a range that does not contain 0.
+                let size = bytes_for_varint(unsafe { NonZeroUsize::new_unchecked(len) }) + len;
+                let align = 2;
+                // SAFETY: align is not zero, align is a power of two, and
+                // rounding size up to align does not overflow usize::MAX.
+                let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
+                // SAFETY: layout's size is nonzero.
+                let ptr = unsafe { alloc(layout) };
+                let mut write = ptr;
+                let mut varint_remaining = len;
+                while varint_remaining > 0 {
+                    // SAFETY: size is bytes_for_varint(len) bytes + len bytes.
+                    // This is writing the first bytes_for_varint(len) bytes.
+                    unsafe { ptr::write(write, varint_remaining as u8 | 0x80) };
+                    varint_remaining >>= 7;
+                    // SAFETY: still in bounds of the same allocation.
+                    write = unsafe { write.add(1) };
+                }
+                // SAFETY: size is bytes_for_varint(len) bytes + len bytes. This
+                // is writing to the last len bytes.
+                unsafe { ptr::copy_nonoverlapping(string.as_ptr(), write, len) };
+                ptr_to_repr(ptr)
+            }
+            0x100_0000_0000_0000..=0xffff_ffff_ffff_ffff => {
+                unreachable!("please refrain from storing >64 petabytes of text in semver version");
+            }
+            #[cfg(no_exhaustive_int_match)] // rustc <1.33
+            _ => unreachable!(),
+        };
+        Identifier { repr }
+    }
+
+    pub(crate) fn is_empty(&self) -> bool {
+        // `cmp rdi, -1` -- basically: `repr as i64 == -1`
+        self.repr == Self::EMPTY
+    }
+
+    fn is_inline(&self) -> bool {
+        // `test rdi, rdi` -- basically: `repr as i64 >= 0`
+        self.repr.get() >> 63 == 0
+    }
+
+    fn is_empty_or_inline(&self) -> bool {
+        // `cmp rdi, -2` -- basically: `repr as i64 > -2`
+        self.is_empty() || self.is_inline()
+    }
+
+    pub(crate) fn as_str(&self) -> &str {
+        if self.is_empty() {
+            ""
+        } else if self.is_inline() {
+            // SAFETY: repr is in the inline representation.
+            unsafe { inline_as_str(&self.repr) }
+        } else {
+            // SAFETY: repr is in the heap allocated representation.
+            unsafe { ptr_as_str(&self.repr) }
+        }
+    }
+}
+
+impl Clone for Identifier {
+    fn clone(&self) -> Self {
+        let repr = if self.is_empty_or_inline() {
+            self.repr
+        } else {
+            let ptr = repr_to_ptr(self.repr);
+            // SAFETY: ptr is one of our own heap allocations.
+            let len = unsafe { decode_len(ptr) };
+            let size = bytes_for_varint(len) + len.get();
+            let align = 2;
+            // SAFETY: align is not zero, align is a power of two, and rounding
+            // size up to align does not overflow usize::MAX. This is just
+            // duplicating a previous allocation where all of these guarantees
+            // were already made.
+            let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
+            // SAFETY: layout's size is nonzero.
+            let clone = unsafe { alloc(layout) };
+            // SAFETY: new allocation cannot overlap the previous one (this was
+            // not a realloc). The argument ptrs are readable/writeable
+            // respectively for size bytes.
+            unsafe { ptr::copy_nonoverlapping(ptr, clone, size) }
+            ptr_to_repr(clone)
+        };
+        Identifier { repr }
+    }
+}
+
+impl Drop for Identifier {
+    fn drop(&mut self) {
+        if self.is_empty_or_inline() {
+            return;
+        }
+        let ptr = repr_to_ptr_mut(self.repr);
+        // SAFETY: ptr is one of our own heap allocations.
+        let len = unsafe { decode_len(ptr) };
+        let size = bytes_for_varint(len) + len.get();
+        let align = 2;
+        // SAFETY: align is not zero, align is a power of two, and rounding
+        // size up to align does not overflow usize::MAX. These guarantees were
+        // made when originally allocating this memory.
+        let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
+        // SAFETY: ptr was previously allocated by the same allocator with the
+        // same layout.
+        unsafe { dealloc(ptr, layout) }
+    }
+}
+
+impl PartialEq for Identifier {
+    fn eq(&self, rhs: &Self) -> bool {
+        if self.is_empty_or_inline() {
+            // Fast path (most common)
+            self.repr == rhs.repr
+        } else if rhs.is_empty_or_inline() {
+            false
+        } else {
+            // SAFETY: both reprs are in the heap allocated representation.
+            unsafe { ptr_as_str(&self.repr) == ptr_as_str(&rhs.repr) }
+        }
+    }
+}
+
+// We use heap pointers that are 2-byte aligned, meaning they have an
+// insignificant 0 in the least significant bit. We take advantage of that
+// unneeded bit to rotate a 1 into the most significant bit to make the repr
+// distinguishable from ASCII bytes.
+fn ptr_to_repr(ptr: *mut u8) -> NonZeroU64 {
+    // `mov eax, 1`
+    // `shld rax, rdi, 63`
+    let repr = (ptr as u64 | 1).rotate_right(1);
+
+    // SAFETY: the most significant bit of repr is known to be set, so the value
+    // is not zero.
+    unsafe { NonZeroU64::new_unchecked(repr) }
+}
+
+// Shift out the 1 previously placed into the most significant bit of the least
+// significant byte. Shift in a low 0 bit to reconstruct the original 2-byte
+// aligned pointer.
+fn repr_to_ptr(repr: NonZeroU64) -> *const u8 {
+    // `lea rax, [rdi + rdi]`
+    (repr.get() << 1) as *const u8
+}
+
+fn repr_to_ptr_mut(repr: NonZeroU64) -> *mut u8 {
+    repr_to_ptr(repr) as *mut u8
+}
+
+// Compute the length of the inline string, assuming the argument is in short
+// string representation. Short strings are stored as 1 to 8 nonzero ASCII
+// bytes, followed by \0 padding for the remaining bytes.
+fn inline_len(repr: NonZeroU64) -> NonZeroUsize {
+    // Rustc >=1.53 has intrinsics for counting zeros on a non-zeroable integer.
+    // On many architectures these are more efficient than counting on ordinary
+    // zeroable integers (bsf vs cttz). On rustc <1.53 without those intrinsics,
+    // we count zeros in the u64 rather than the NonZeroU64.
+    #[cfg(no_nonzero_bitscan)]
+    let repr = repr.get();
+
+    #[cfg(target_endian = "little")]
+    let zero_bits_on_string_end = repr.leading_zeros();
+    #[cfg(target_endian = "big")]
+    let zero_bits_on_string_end = repr.trailing_zeros();
+
+    let nonzero_bytes = 8 - zero_bits_on_string_end as usize / 8;
+
+    // SAFETY: repr is nonzero, so it has at most 63 zero bits on either end,
+    // thus at least one nonzero byte.
+    unsafe { NonZeroUsize::new_unchecked(nonzero_bytes) }
+}
+
+// SAFETY: repr must be in the inline representation, i.e. at least 1 and at
+// most 8 nonzero ASCII bytes padded on the end with \0 bytes.
+unsafe fn inline_as_str(repr: &NonZeroU64) -> &str {
+    let ptr = repr as *const NonZeroU64 as *const u8;
+    let len = inline_len(*repr).get();
+    // SAFETY: we are viewing the nonzero ASCII prefix of the inline repr's
+    // contents as a slice of bytes. Input/output lifetimes are correctly
+    // associated.
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    // SAFETY: the string contents are known to be only ASCII bytes, which are
+    // always valid UTF-8.
+    unsafe { str::from_utf8_unchecked(slice) }
+}
+
+// Decode varint. Varints consist of between one and eight base-128 digits, each
+// of which is stored in a byte with most significant bit set. Adjacent to the
+// varint in memory there is guaranteed to be at least 9 ASCII bytes, each of
+// which has an unset most significant bit.
+//
+// SAFETY: ptr must be one of our own heap allocations, with the varint header
+// already written.
+unsafe fn decode_len(ptr: *const u8) -> NonZeroUsize {
+    // SAFETY: There is at least one byte of varint followed by at least 9 bytes
+    // of string content, which is at least 10 bytes total for the allocation,
+    // so reading the first two is no problem.
+    let [first, second] = unsafe { ptr::read(ptr as *const [u8; 2]) };
+    if second < 0x80 {
+        // SAFETY: the length of this heap allocated string has been encoded as
+        // one base-128 digit, so the length is at least 9 and at most 127. It
+        // cannot be zero.
+        unsafe { NonZeroUsize::new_unchecked((first & 0x7f) as usize) }
+    } else {
+        return unsafe { decode_len_cold(ptr) };
+
+        // Identifiers 128 bytes or longer. This is not exercised by any crate
+        // version currently published to crates.io.
+        #[cold]
+        #[inline(never)]
+        unsafe fn decode_len_cold(mut ptr: *const u8) -> NonZeroUsize {
+            let mut len = 0;
+            let mut shift = 0;
+            loop {
+                // SAFETY: varint continues while there are bytes having the
+                // most significant bit set, i.e. until we start hitting the
+                // ASCII string content with msb unset.
+                let byte = unsafe { *ptr };
+                if byte < 0x80 {
+                    // SAFETY: the string length is known to be 128 bytes or
+                    // longer.
+                    return unsafe { NonZeroUsize::new_unchecked(len) };
+                }
+                // SAFETY: still in bounds of the same allocation.
+                ptr = unsafe { ptr.add(1) };
+                len += ((byte & 0x7f) as usize) << shift;
+                shift += 7;
+            }
+        }
+    }
+}
+
+// SAFETY: repr must be in the heap allocated representation, with varint header
+// and string contents already written.
+unsafe fn ptr_as_str(repr: &NonZeroU64) -> &str {
+    let ptr = repr_to_ptr(*repr);
+    let len = unsafe { decode_len(ptr) };
+    let header = bytes_for_varint(len);
+    let slice = unsafe { slice::from_raw_parts(ptr.add(header), len.get()) };
+    // SAFETY: all identifier contents are ASCII bytes, which are always valid
+    // UTF-8.
+    unsafe { str::from_utf8_unchecked(slice) }
+}
+
+// Number of base-128 digits required for the varint representation of a length.
+fn bytes_for_varint(len: NonZeroUsize) -> usize {
+    #[cfg(no_nonzero_bitscan)] // rustc <1.53
+    let len = len.get();
+
+    let usize_bits = mem::size_of::<usize>() * 8;
+    let len_bits = usize_bits - len.leading_zeros() as usize;
+    (len_bits + 6) / 7
+}
diff --git a/third_party/rust/semver/v1/crate/src/impls.rs b/third_party/rust/semver/v1/crate/src/impls.rs
new file mode 100644
index 0000000..c3b6c60
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/impls.rs
@@ -0,0 +1,156 @@
+use crate::backport::*;
+use crate::identifier::Identifier;
+use crate::{BuildMetadata, Comparator, Prerelease, VersionReq};
+use core::cmp::Ordering;
+use core::hash::{Hash, Hasher};
+use core::iter::FromIterator;
+use core::ops::Deref;
+
+impl Default for Identifier {
+    fn default() -> Self {
+        Identifier::empty()
+    }
+}
+
+impl Eq for Identifier {}
+
+impl Hash for Identifier {
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        self.as_str().hash(hasher);
+    }
+}
+
+impl Deref for Prerelease {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        self.identifier.as_str()
+    }
+}
+
+impl Deref for BuildMetadata {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        self.identifier.as_str()
+    }
+}
+
+impl PartialOrd for Prerelease {
+    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
+        Some(Ord::cmp(self, rhs))
+    }
+}
+
+impl PartialOrd for BuildMetadata {
+    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
+        Some(Ord::cmp(self, rhs))
+    }
+}
+
+impl Ord for Prerelease {
+    fn cmp(&self, rhs: &Self) -> Ordering {
+        match self.is_empty() {
+            true if rhs.is_empty() => return Ordering::Equal,
+            // A real release compares greater than prerelease.
+            true => return Ordering::Greater,
+            // Prerelease compares less than the real release.
+            false if rhs.is_empty() => return Ordering::Less,
+            false => {}
+        }
+
+        let lhs = self.as_str().split('.');
+        let mut rhs = rhs.as_str().split('.');
+
+        for lhs in lhs {
+            let rhs = match rhs.next() {
+                // Spec: "A larger set of pre-release fields has a higher
+                // precedence than a smaller set, if all of the preceding
+                // identifiers are equal."
+                None => return Ordering::Greater,
+                Some(rhs) => rhs,
+            };
+
+            let string_cmp = || Ord::cmp(lhs, rhs);
+            let is_ascii_digit = |b: u8| b.is_ascii_digit();
+            let ordering = match (
+                lhs.bytes().all(is_ascii_digit),
+                rhs.bytes().all(is_ascii_digit),
+            ) {
+                // Respect numeric ordering, for example 99 < 100. Spec says:
+                // "Identifiers consisting of only digits are compared
+                // numerically."
+                (true, true) => Ord::cmp(&lhs.len(), &rhs.len()).then_with(string_cmp),
+                // Spec: "Numeric identifiers always have lower precedence than
+                // non-numeric identifiers."
+                (true, false) => return Ordering::Less,
+                (false, true) => return Ordering::Greater,
+                // Spec: "Identifiers with letters or hyphens are compared
+                // lexically in ASCII sort order."
+                (false, false) => string_cmp(),
+            };
+
+            if ordering != Ordering::Equal {
+                return ordering;
+            }
+        }
+
+        if rhs.next().is_none() {
+            Ordering::Equal
+        } else {
+            Ordering::Less
+        }
+    }
+}
+
+impl Ord for BuildMetadata {
+    fn cmp(&self, rhs: &Self) -> Ordering {
+        let lhs = self.as_str().split('.');
+        let mut rhs = rhs.as_str().split('.');
+
+        for lhs in lhs {
+            let rhs = match rhs.next() {
+                None => return Ordering::Greater,
+                Some(rhs) => rhs,
+            };
+
+            let is_ascii_digit = |b: u8| b.is_ascii_digit();
+            let ordering = match (
+                lhs.bytes().all(is_ascii_digit),
+                rhs.bytes().all(is_ascii_digit),
+            ) {
+                (true, true) => {
+                    // 0 < 00 < 1 < 01 < 001 < 2 < 02 < 002 < 10
+                    let lhval = lhs.trim_start_matches('0');
+                    let rhval = rhs.trim_start_matches('0');
+                    Ord::cmp(&lhval.len(), &rhval.len())
+                        .then_with(|| Ord::cmp(lhval, rhval))
+                        .then_with(|| Ord::cmp(&lhs.len(), &rhs.len()))
+                }
+                (true, false) => return Ordering::Less,
+                (false, true) => return Ordering::Greater,
+                (false, false) => Ord::cmp(lhs, rhs),
+            };
+
+            if ordering != Ordering::Equal {
+                return ordering;
+            }
+        }
+
+        if rhs.next().is_none() {
+            Ordering::Equal
+        } else {
+            Ordering::Less
+        }
+    }
+}
+
+impl FromIterator<Comparator> for VersionReq {
+    fn from_iter<I>(iter: I) -> Self
+    where
+        I: IntoIterator<Item = Comparator>,
+    {
+        let comparators = Vec::from_iter(iter);
+        VersionReq { comparators }
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/src/lib.rs b/third_party/rust/semver/v1/crate/src/lib.rs
new file mode 100644
index 0000000..adfd37d4
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/lib.rs
@@ -0,0 +1,523 @@
+//! [![github]](https://github.com/dtolnay/semver)&ensp;[![crates-io]](https://crates.io/crates/semver)&ensp;[![docs-rs]](https://docs.rs/semver)
+//!
+//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
+//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
+//!
+//! <br>
+//!
+//! A parser and evaluator for Cargo's flavor of Semantic Versioning.
+//!
+//! Semantic Versioning (see <https://semver.org>) is a guideline for how
+//! version numbers are assigned and incremented. It is widely followed within
+//! the Cargo/crates.io ecosystem for Rust.
+//!
+//! <br>
+//!
+//! # Example
+//!
+//! ```
+//! use semver::{BuildMetadata, Prerelease, Version, VersionReq};
+//!
+//! fn main() {
+//!     let req = VersionReq::parse(">=1.2.3, <1.8.0").unwrap();
+//!
+//!     // Check whether this requirement matches version 1.2.3-alpha.1 (no)
+//!     let version = Version {
+//!         major: 1,
+//!         minor: 2,
+//!         patch: 3,
+//!         pre: Prerelease::new("alpha.1").unwrap(),
+//!         build: BuildMetadata::EMPTY,
+//!     };
+//!     assert!(!req.matches(&version));
+//!
+//!     // Check whether it matches 1.3.0 (yes it does)
+//!     let version = Version::parse("1.3.0").unwrap();
+//!     assert!(req.matches(&version));
+//! }
+//! ```
+//!
+//! <br><br>
+//!
+//! # Scope of this crate
+//!
+//! Besides Cargo, several other package ecosystems and package managers for
+//! other languages also use SemVer:&ensp;RubyGems/Bundler for Ruby, npm for
+//! JavaScript, Composer for PHP, CocoaPods for Objective-C...
+//!
+//! The `semver` crate is specifically intended to implement Cargo's
+//! interpretation of Semantic Versioning.
+//!
+//! Where the various tools differ in their interpretation or implementation of
+//! the spec, this crate follows the implementation choices made by Cargo. If
+//! you are operating on version numbers from some other package ecosystem, you
+//! will want to use a different semver library which is appropriate to that
+//! ecosystem.
+//!
+//! The extent of Cargo's SemVer support is documented in the *[Specifying
+//! Dependencies]* chapter of the Cargo reference.
+//!
+//! [Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
+
+#![doc(html_root_url = "https://docs.rs/semver/1.0.4")]
+#![cfg_attr(doc_cfg, feature(doc_cfg))]
+#![cfg_attr(all(not(feature = "std"), not(no_alloc_crate)), no_std)]
+#![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))]
+#![cfg_attr(no_unsafe_op_in_unsafe_fn_lint, allow(unused_unsafe))]
+#![cfg_attr(no_str_strip_prefix, allow(unstable_name_collisions))]
+#![allow(
+    clippy::cast_lossless,
+    clippy::cast_possible_truncation,
+    clippy::doc_markdown,
+    clippy::items_after_statements,
+    clippy::match_bool,
+    clippy::missing_errors_doc,
+    clippy::must_use_candidate,
+    clippy::needless_doctest_main,
+    clippy::option_if_let_else,
+    clippy::ptr_as_ptr,
+    clippy::redundant_else,
+    clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
+    clippy::similar_names,
+    clippy::unnested_or_patterns,
+    clippy::unseparated_literal_suffix,
+    clippy::wildcard_imports
+)]
+
+#[cfg(not(no_alloc_crate))]
+extern crate alloc;
+
+mod backport;
+mod display;
+mod error;
+mod eval;
+mod identifier;
+mod impls;
+mod parse;
+
+#[cfg(feature = "serde")]
+mod serde;
+
+use crate::alloc::vec::Vec;
+use crate::identifier::Identifier;
+use core::str::FromStr;
+
+#[allow(unused_imports)]
+use crate::backport::*;
+
+pub use crate::parse::Error;
+
+/// **SemVer version** as defined by <https://semver.org>.
+///
+/// # Syntax
+///
+/// - The major, minor, and patch numbers may be any integer 0 through u64::MAX.
+///   When representing a SemVer version as a string, each number is written as
+///   a base 10 integer. For example, `1.0.119`.
+///
+/// - Leading zeros are forbidden in those positions. For example `1.01.00` is
+///   invalid as a SemVer version.
+///
+/// - The pre-release identifier, if present, must conform to the syntax
+///   documented for [`Prerelease`].
+///
+/// - The build metadata, if present, must conform to the syntax documented for
+///   [`BuildMetadata`].
+///
+/// - Whitespace is not allowed anywhere in the version.
+///
+/// # Total ordering
+///
+/// Given any two SemVer versions, one is less than, greater than, or equal to
+/// the other. Versions may be compared against one another using Rust's usual
+/// comparison operators.
+///
+/// - The major, minor, and patch number are compared numerically from left to
+/// right, lexicographically ordered as a 3-tuple of integers. So for example
+/// version `1.5.0` is less than version `1.19.0`, despite the fact that
+/// "1.19.0" &lt; "1.5.0" as ASCIIbetically compared strings and 1.19 &lt; 1.5
+/// as real numbers.
+///
+/// - When major, minor, and patch are equal, a pre-release version is
+///   considered less than the ordinary release:&ensp;version `1.0.0-alpha.1` is
+///   less than version `1.0.0`.
+///
+/// - Two pre-releases of the same major, minor, patch are compared by
+///   lexicographic ordering of dot-separated components of the pre-release
+///   string.
+///
+///   - Identifiers consisting of only digits are compared
+///     numerically:&ensp;`1.0.0-pre.8` is less than `1.0.0-pre.12`.
+///
+///   - Identifiers that contain a letter or hyphen are compared in ASCII sort
+///     order:&ensp;`1.0.0-pre12` is less than `1.0.0-pre8`.
+///
+///   - Any numeric identifier is always less than any non-numeric
+///     identifier:&ensp;`1.0.0-pre.1` is less than `1.0.0-pre.x`.
+///
+/// Example:&ensp;`1.0.0-alpha`&ensp;&lt;&ensp;`1.0.0-alpha.1`&ensp;&lt;&ensp;`1.0.0-alpha.beta`&ensp;&lt;&ensp;`1.0.0-beta`&ensp;&lt;&ensp;`1.0.0-beta.2`&ensp;&lt;&ensp;`1.0.0-beta.11`&ensp;&lt;&ensp;`1.0.0-rc.1`&ensp;&lt;&ensp;`1.0.0`
+#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Version {
+    pub major: u64,
+    pub minor: u64,
+    pub patch: u64,
+    pub pre: Prerelease,
+    pub build: BuildMetadata,
+}
+
+/// **SemVer version requirement** describing the intersection of some version
+/// comparators, such as `>=1.2.3, <1.8`.
+///
+/// # Syntax
+///
+/// - Either `*` (meaning "any"), or one or more comma-separated comparators.
+///
+/// - A [`Comparator`] is an operator ([`Op`]) and a partial version, separated
+///   by optional whitespace. For example `>=1.0.0` or `>=1.0`.
+///
+/// - Build metadata is syntactically permitted on the partial versions, but is
+///   completely ignored, as it's never relevant to whether any comparator
+///   matches a particular version.
+///
+/// - Whitespace is permitted around commas and around operators. Whitespace is
+///   not permitted within a partial version, i.e. anywhere between the major
+///   version number and its minor, patch, pre-release, or build metadata.
+#[derive(Default, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct VersionReq {
+    pub comparators: Vec<Comparator>,
+}
+
+/// A pair of comparison operator and partial version, such as `>=1.2`. Forms
+/// one piece of a VersionReq.
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct Comparator {
+    pub op: Op,
+    pub major: u64,
+    pub minor: Option<u64>,
+    /// Patch is only allowed if minor is Some.
+    pub patch: Option<u64>,
+    /// Non-empty pre-release is only allowed if patch is Some.
+    pub pre: Prerelease,
+}
+
+/// SemVer comparison operator: `=`, `>`, `>=`, `<`, `<=`, `~`, `^`, `*`.
+///
+/// # Op::Exact
+/// - &ensp;**`=I.J.K`**&emsp;&mdash;&emsp;exactly the version I.J.K
+/// - &ensp;**`=I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.J.0, <I.(J+1).0`
+/// - &ensp;**`=I`**&emsp;&mdash;&emsp;equivalent to `>=I.0.0, <(I+1).0.0`
+///
+/// # Op::Greater
+/// - &ensp;**`>I.J.K`**
+/// - &ensp;**`>I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.(J+1).0`
+/// - &ensp;**`>I`**&emsp;&mdash;&emsp;equivalent to `>=(I+1).0.0`
+///
+/// # Op::GreaterEq
+/// - &ensp;**`>=I.J.K`**
+/// - &ensp;**`>=I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.J.0`
+/// - &ensp;**`>=I`**&emsp;&mdash;&emsp;equivalent to `>=I.0.0`
+///
+/// # Op::Less
+/// - &ensp;**`<I.J.K`**
+/// - &ensp;**`<I.J`**&emsp;&mdash;&emsp;equivalent to `<I.J.0`
+/// - &ensp;**`<I`**&emsp;&mdash;&emsp;equivalent to `<I.0.0`
+///
+/// # Op::LessEq
+/// - &ensp;**`<=I.J.K`**
+/// - &ensp;**`<=I.J`**&emsp;&mdash;&emsp;equivalent to `<I.(J+1).0`
+/// - &ensp;**`<=I`**&emsp;&mdash;&emsp;equivalent to `<(I+1).0.0`
+///
+/// # Op::Tilde&emsp;("patch" updates)
+/// *Tilde requirements allow the **patch** part of the semver version (the third number) to increase.*
+/// - &ensp;**`~I.J.K`**&emsp;&mdash;&emsp;equivalent to `>=I.J.K, <I.(J+1).0`
+/// - &ensp;**`~I.J`**&emsp;&mdash;&emsp;equivalent to `=I.J`
+/// - &ensp;**`~I`**&emsp;&mdash;&emsp;equivalent to `=I`
+///
+/// # Op::Caret&emsp;("compatible" updates)
+/// *Caret requirements allow parts that are **right of the first nonzero** part of the semver version to increase.*
+/// - &ensp;**`^I.J.K`**&ensp;(for I\>0)&emsp;&mdash;&emsp;equivalent to `>=I.J.K, <(I+1).0.0`
+/// - &ensp;**`^0.J.K`**&ensp;(for J\>0)&emsp;&mdash;&emsp;equivalent to `>=0.J.K, <0.(J+1).0`
+/// - &ensp;**`^0.0.K`**&emsp;&mdash;&emsp;equivalent to `=0.0.K`
+/// - &ensp;**`^I.J`**&ensp;(for I\>0 or J\>0)&emsp;&mdash;&emsp;equivalent to `^I.J.0`
+/// - &ensp;**`^0.0`**&emsp;&mdash;&emsp;equivalent to `=0.0`
+/// - &ensp;**`^I`**&emsp;&mdash;&emsp;equivalent to `=I`
+///
+/// # Op::Wildcard
+/// - &ensp;**`I.J.*`**&emsp;&mdash;&emsp;equivalent to `=I.J`
+/// - &ensp;**`I.*`**&ensp;or&ensp;**`I.*.*`**&emsp;&mdash;&emsp;equivalent to `=I`
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+#[cfg_attr(not(no_non_exhaustive), non_exhaustive)]
+pub enum Op {
+    Exact,
+    Greater,
+    GreaterEq,
+    Less,
+    LessEq,
+    Tilde,
+    Caret,
+    Wildcard,
+
+    #[cfg(no_non_exhaustive)] // rustc <1.40
+    #[doc(hidden)]
+    __NonExhaustive,
+}
+
+/// Optional pre-release identifier on a version string. This comes after `-` in
+/// a SemVer version, like `1.0.0-alpha.1`
+///
+/// # Examples
+///
+/// Some real world pre-release idioms drawn from crates.io:
+///
+/// - **[mio]** <code>0.7.0-<b>alpha.1</b></code> &mdash; the most common style
+///   for numbering pre-releases.
+///
+/// - **[pest]** <code>1.0.0-<b>beta.8</b></code>,&ensp;<code>1.0.0-<b>rc.0</b></code>
+///   &mdash; this crate makes a distinction between betas and release
+///   candidates.
+///
+/// - **[sassers]** <code>0.11.0-<b>shitshow</b></code> &mdash; ???.
+///
+/// - **[atomic-utils]** <code>0.0.0-<b>reserved</b></code> &mdash; a squatted
+///   crate name.
+///
+/// [mio]: https://crates.io/crates/mio
+/// [pest]: https://crates.io/crates/pest
+/// [atomic-utils]: https://crates.io/crates/atomic-utils
+/// [sassers]: https://crates.io/crates/sassers
+///
+/// *Tip:* Be aware that if you are planning to number your own pre-releases,
+/// you should prefer to separate the numeric part from any non-numeric
+/// identifiers by using a dot in between. That is, prefer pre-releases
+/// `alpha.1`, `alpha.2`, etc rather than `alpha1`, `alpha2` etc. The SemVer
+/// spec's rule for pre-release precedence has special treatment of numeric
+/// components in the pre-release string, but only if there are no non-digit
+/// characters in the same dot-separated component. So you'd have `alpha.2` &lt;
+/// `alpha.11` as intended, but `alpha11` &lt; `alpha2`.
+///
+/// # Syntax
+///
+/// Pre-release strings are a series of dot separated identifiers immediately
+/// following the patch version. Identifiers must comprise only ASCII
+/// alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must not be
+/// empty. Numeric identifiers must not include leading zeros.
+///
+/// # Total ordering
+///
+/// Pre-releases have a total order defined by the SemVer spec. It uses
+/// lexicographic ordering of dot-separated components. Identifiers consisting
+/// of only digits are compared numerically. Otherwise, identifiers are compared
+/// in ASCII sort order. Any numeric identifier is always less than any
+/// non-numeric identifier.
+///
+/// Example:&ensp;`alpha`&ensp;&lt;&ensp;`alpha.85`&ensp;&lt;&ensp;`alpha.90`&ensp;&lt;&ensp;`alpha.200`&ensp;&lt;&ensp;`alpha.0a`&ensp;&lt;&ensp;`alpha.1a0`&ensp;&lt;&ensp;`alpha.a`&ensp;&lt;&ensp;`beta`
+#[derive(Default, Clone, Eq, PartialEq, Hash)]
+pub struct Prerelease {
+    identifier: Identifier,
+}
+
+/// Optional build metadata identifier. This comes after `+` in a SemVer
+/// version, as in `0.8.1+zstd.1.5.0`.
+///
+/// # Examples
+///
+/// Some real world build metadata idioms drawn from crates.io:
+///
+/// - **[libgit2-sys]** <code>0.12.20+<b>1.1.0</b></code> &mdash; for this
+///   crate, the build metadata indicates the version of the C libgit2 library
+///   that the Rust crate is built against.
+///
+/// - **[mashup]** <code>0.1.13+<b>deprecated</b></code> &mdash; just the word
+///   "deprecated" for a crate that has been superseded by another. Eventually
+///   people will take notice of this in Cargo's build output where it lists the
+///   crates being compiled.
+///
+/// - **[google-bigquery2]** <code>2.0.4+<b>20210327</b></code> &mdash; this
+///   library is automatically generated from an official API schema, and the
+///   build metadata indicates the date on which that schema was last captured.
+///
+/// - **[fbthrift-git]** <code>0.0.6+<b>c7fcc0e</b></code> &mdash; this crate is
+///   published from snapshots of a big company monorepo. In monorepo
+///   development, there is no concept of versions, and all downstream code is
+///   just updated atomically in the same commit that breaking changes to a
+///   library are landed. Therefore for crates.io purposes, every published
+///   version must be assumed to be incompatible with the previous. The build
+///   metadata provides the source control hash of the snapshotted code.
+///
+/// [libgit2-sys]: https://crates.io/crates/libgit2-sys
+/// [mashup]: https://crates.io/crates/mashup
+/// [google-bigquery2]: https://crates.io/crates/google-bigquery2
+/// [fbthrift-git]: https://crates.io/crates/fbthrift-git
+///
+/// # Syntax
+///
+/// Build metadata is a series of dot separated identifiers immediately
+/// following the patch or pre-release version. Identifiers must comprise only
+/// ASCII alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must
+/// not be empty. Leading zeros *are* allowed, unlike any other place in the
+/// SemVer grammar.
+///
+/// # Total ordering
+///
+/// Build metadata is ignored in evaluating `VersionReq`; it plays no role in
+/// whether a `Version` matches any one of the comparison operators.
+///
+/// However for comparing build metadatas among one another, they do have a
+/// total order which is determined by lexicographic ordering of dot-separated
+/// components. Identifiers consisting of only digits are compared numerically.
+/// Otherwise, identifiers are compared in ASCII sort order. Any numeric
+/// identifier is always less than any non-numeric identifier.
+///
+/// Example:&ensp;`demo`&ensp;&lt;&ensp;`demo.85`&ensp;&lt;&ensp;`demo.90`&ensp;&lt;&ensp;`demo.090`&ensp;&lt;&ensp;`demo.200`&ensp;&lt;&ensp;`demo.1a0`&ensp;&lt;&ensp;`demo.a`&ensp;&lt;&ensp;`memo`
+#[derive(Default, Clone, Eq, PartialEq, Hash)]
+pub struct BuildMetadata {
+    identifier: Identifier,
+}
+
+impl Version {
+    /// Create `Version` with an empty pre-release and build metadata.
+    ///
+    /// Equivalent to:
+    ///
+    /// ```
+    /// # use semver::{BuildMetadata, Prerelease, Version};
+    /// #
+    /// # const fn new(major: u64, minor: u64, patch: u64) -> Version {
+    /// Version {
+    ///     major,
+    ///     minor,
+    ///     patch,
+    ///     pre: Prerelease::EMPTY,
+    ///     build: BuildMetadata::EMPTY,
+    /// }
+    /// # }
+    /// ```
+    pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
+        Version {
+            major,
+            minor,
+            patch,
+            pre: Prerelease::EMPTY,
+            build: BuildMetadata::EMPTY,
+        }
+    }
+
+    /// Create `Version` by parsing from string representation.
+    ///
+    /// # Errors
+    ///
+    /// Possible reasons for the parse to fail include:
+    ///
+    /// - `1.0` &mdash; too few numeric components. A SemVer version must have
+    ///   exactly three. If you are looking at something that has fewer than
+    ///   three numbers in it, it's possible it is a `VersionReq` instead (with
+    ///   an implicit default `^` comparison operator).
+    ///
+    /// - `1.0.01` &mdash; a numeric component has a leading zero.
+    ///
+    /// - `1.0.unknown` &mdash; unexpected character in one of the components.
+    ///
+    /// - `1.0.0-` or `1.0.0+` &mdash; the pre-release or build metadata are
+    ///   indicated present but empty.
+    ///
+    /// - `1.0.0-alpha_123` &mdash; pre-release or build metadata have something
+    ///   outside the allowed characters, which are `0-9`, `A-Z`, `a-z`, `-`,
+    ///   and `.` (dot).
+    ///
+    /// - `23456789999999999999.0.0` &mdash; overflow of a u64.
+    pub fn parse(text: &str) -> Result<Self, Error> {
+        Version::from_str(text)
+    }
+}
+
+impl VersionReq {
+    /// A `VersionReq` with no constraint on the version numbers it matches.
+    /// Equivalent to `VersionReq::parse("*").unwrap()`.
+    ///
+    /// In terms of comparators this is equivalent to `>=0.0.0`.
+    ///
+    /// Counterintuitively a `*` VersionReq does not match every possible
+    /// version number. In particular, in order for *any* `VersionReq` to match
+    /// a pre-release version, the `VersionReq` must contain at least one
+    /// `Comparator` that has an explicit major, minor, and patch version
+    /// identical to the pre-release being matched, and that has a nonempty
+    /// pre-release component. Since `*` is not written with an explicit major,
+    /// minor, and patch version, and does not contain a nonempty pre-release
+    /// component, it does not match any pre-release versions.
+    #[cfg(not(no_const_vec_new))] // rustc <1.39
+    pub const STAR: Self = VersionReq {
+        comparators: Vec::new(),
+    };
+
+    /// Create `VersionReq` by parsing from string representation.
+    ///
+    /// # Errors
+    ///
+    /// Possible reasons for the parse to fail include:
+    ///
+    /// - `>a.b` &mdash; unexpected characters in the partial version.
+    ///
+    /// - `@1.0.0` &mdash; unrecognized comparison operator.
+    ///
+    /// - `^1.0.0, ` &mdash; unexpected end of input.
+    ///
+    /// - `>=1.0 <2.0` &mdash; missing comma between comparators.
+    ///
+    /// - `*.*` &mdash; unsupported wildcard syntax.
+    pub fn parse(text: &str) -> Result<Self, Error> {
+        VersionReq::from_str(text)
+    }
+
+    /// Evaluate whether the given `Version` satisfies the version requirement
+    /// described by `self`.
+    pub fn matches(&self, version: &Version) -> bool {
+        eval::matches_req(self, version)
+    }
+}
+
+impl Comparator {
+    pub fn parse(text: &str) -> Result<Self, Error> {
+        Comparator::from_str(text)
+    }
+
+    pub fn matches(&self, version: &Version) -> bool {
+        eval::matches_comparator(self, version)
+    }
+}
+
+impl Prerelease {
+    pub const EMPTY: Self = Prerelease {
+        identifier: Identifier::empty(),
+    };
+
+    pub fn new(text: &str) -> Result<Self, Error> {
+        Prerelease::from_str(text)
+    }
+
+    pub fn as_str(&self) -> &str {
+        self.identifier.as_str()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.identifier.is_empty()
+    }
+}
+
+impl BuildMetadata {
+    pub const EMPTY: Self = BuildMetadata {
+        identifier: Identifier::empty(),
+    };
+
+    pub fn new(text: &str) -> Result<Self, Error> {
+        BuildMetadata::from_str(text)
+    }
+
+    pub fn as_str(&self) -> &str {
+        self.identifier.as_str()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.identifier.is_empty()
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/src/parse.rs b/third_party/rust/semver/v1/crate/src/parse.rs
new file mode 100644
index 0000000..017a5b5
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/parse.rs
@@ -0,0 +1,391 @@
+use crate::backport::*;
+use crate::error::{ErrorKind, Position};
+use crate::identifier::Identifier;
+use crate::{BuildMetadata, Comparator, Op, Prerelease, Version, VersionReq};
+use core::str::FromStr;
+
+/// Error parsing a SemVer version or version requirement.
+///
+/// # Example
+///
+/// ```
+/// use semver::Version;
+///
+/// fn main() {
+///     let err = Version::parse("1.q.r").unwrap_err();
+///
+///     // "unexpected character 'q' while parsing minor version number"
+///     eprintln!("{}", err);
+/// }
+/// ```
+pub struct Error {
+    pub(crate) kind: ErrorKind,
+}
+
+impl FromStr for Version {
+    type Err = Error;
+
+    fn from_str(text: &str) -> Result<Self, Self::Err> {
+        let mut pos = Position::Major;
+        let (major, text) = numeric_identifier(text, pos)?;
+        let text = dot(text, pos)?;
+
+        pos = Position::Minor;
+        let (minor, text) = numeric_identifier(text, pos)?;
+        let text = dot(text, pos)?;
+
+        pos = Position::Patch;
+        let (patch, text) = numeric_identifier(text, pos)?;
+
+        if text.is_empty() {
+            return Ok(Version::new(major, minor, patch));
+        }
+
+        let (pre, text) = if let Some(text) = text.strip_prefix('-') {
+            pos = Position::Pre;
+            let (pre, text) = prerelease_identifier(text)?;
+            if pre.is_empty() {
+                return Err(Error::new(ErrorKind::EmptySegment(pos)));
+            }
+            (pre, text)
+        } else {
+            (Prerelease::EMPTY, text)
+        };
+
+        let (build, text) = if let Some(text) = text.strip_prefix('+') {
+            pos = Position::Build;
+            let (build, text) = build_identifier(text)?;
+            if build.is_empty() {
+                return Err(Error::new(ErrorKind::EmptySegment(pos)));
+            }
+            (build, text)
+        } else {
+            (BuildMetadata::EMPTY, text)
+        };
+
+        if let Some(unexpected) = text.chars().next() {
+            return Err(Error::new(ErrorKind::UnexpectedCharAfter(pos, unexpected)));
+        }
+
+        Ok(Version {
+            major,
+            minor,
+            patch,
+            pre,
+            build,
+        })
+    }
+}
+
+impl FromStr for VersionReq {
+    type Err = Error;
+
+    fn from_str(text: &str) -> Result<Self, Self::Err> {
+        let text = text.trim_start_matches(' ');
+        if let Some(text) = wildcard(text) {
+            if text.trim_start_matches(' ').is_empty() {
+                #[cfg(not(no_const_vec_new))]
+                return Ok(VersionReq::STAR);
+                #[cfg(no_const_vec_new)] // rustc <1.39
+                return Ok(VersionReq {
+                    comparators: Vec::new(),
+                });
+            } else {
+                return Err(Error::new(ErrorKind::UnexpectedAfterWildcard));
+            }
+        }
+
+        let depth = 0;
+        let mut comparators = Vec::new();
+        let len = version_req(text, &mut comparators, depth)?;
+        unsafe { comparators.set_len(len) }
+        Ok(VersionReq { comparators })
+    }
+}
+
+impl FromStr for Comparator {
+    type Err = Error;
+
+    fn from_str(text: &str) -> Result<Self, Self::Err> {
+        let text = text.trim_start_matches(' ');
+        let (comparator, pos, rest) = comparator(text)?;
+        if !rest.is_empty() {
+            let unexpected = rest.chars().next().unwrap();
+            return Err(Error::new(ErrorKind::UnexpectedCharAfter(pos, unexpected)));
+        }
+        Ok(comparator)
+    }
+}
+
+impl FromStr for Prerelease {
+    type Err = Error;
+
+    fn from_str(text: &str) -> Result<Self, Self::Err> {
+        let (pre, rest) = prerelease_identifier(text)?;
+        if !rest.is_empty() {
+            return Err(Error::new(ErrorKind::IllegalCharacter(Position::Pre)));
+        }
+        Ok(pre)
+    }
+}
+
+impl FromStr for BuildMetadata {
+    type Err = Error;
+
+    fn from_str(text: &str) -> Result<Self, Self::Err> {
+        let (build, rest) = build_identifier(text)?;
+        if !rest.is_empty() {
+            return Err(Error::new(ErrorKind::IllegalCharacter(Position::Build)));
+        }
+        Ok(build)
+    }
+}
+
+impl Error {
+    fn new(kind: ErrorKind) -> Self {
+        Error { kind }
+    }
+}
+
+impl Op {
+    const DEFAULT: Self = Op::Caret;
+}
+
+fn numeric_identifier(input: &str, pos: Position) -> Result<(u64, &str), Error> {
+    let mut len = 0;
+    let mut value = 0u64;
+
+    while let Some(&digit) = input.as_bytes().get(len) {
+        if digit < b'0' || digit > b'9' {
+            break;
+        }
+        if value == 0 && len > 0 {
+            return Err(Error::new(ErrorKind::LeadingZero(pos)));
+        }
+        match value
+            .checked_mul(10)
+            .and_then(|value| value.checked_add((digit - b'0') as u64))
+        {
+            Some(sum) => value = sum,
+            None => return Err(Error::new(ErrorKind::Overflow(pos))),
+        }
+        len += 1;
+    }
+
+    if len > 0 {
+        Ok((value, &input[len..]))
+    } else if let Some(unexpected) = input[len..].chars().next() {
+        Err(Error::new(ErrorKind::UnexpectedChar(pos, unexpected)))
+    } else {
+        Err(Error::new(ErrorKind::UnexpectedEnd(pos)))
+    }
+}
+
+fn wildcard(input: &str) -> Option<&str> {
+    if let Some(rest) = input.strip_prefix('*') {
+        Some(rest)
+    } else if let Some(rest) = input.strip_prefix('x') {
+        Some(rest)
+    } else if let Some(rest) = input.strip_prefix('X') {
+        Some(rest)
+    } else {
+        None
+    }
+}
+
+fn dot(input: &str, pos: Position) -> Result<&str, Error> {
+    if let Some(rest) = input.strip_prefix('.') {
+        Ok(rest)
+    } else if let Some(unexpected) = input.chars().next() {
+        Err(Error::new(ErrorKind::UnexpectedCharAfter(pos, unexpected)))
+    } else {
+        Err(Error::new(ErrorKind::UnexpectedEnd(pos)))
+    }
+}
+
+fn prerelease_identifier(input: &str) -> Result<(Prerelease, &str), Error> {
+    let (string, rest) = identifier(input, Position::Pre)?;
+    let identifier = unsafe { Identifier::new_unchecked(string) };
+    Ok((Prerelease { identifier }, rest))
+}
+
+fn build_identifier(input: &str) -> Result<(BuildMetadata, &str), Error> {
+    let (string, rest) = identifier(input, Position::Build)?;
+    let identifier = unsafe { Identifier::new_unchecked(string) };
+    Ok((BuildMetadata { identifier }, rest))
+}
+
+fn identifier(input: &str, pos: Position) -> Result<(&str, &str), Error> {
+    let mut accumulated_len = 0;
+    let mut segment_len = 0;
+    let mut segment_has_nondigit = false;
+
+    loop {
+        match input.as_bytes().get(accumulated_len + segment_len) {
+            Some(b'A'..=b'Z') | Some(b'a'..=b'z') | Some(b'-') => {
+                segment_len += 1;
+                segment_has_nondigit = true;
+            }
+            Some(b'0'..=b'9') => {
+                segment_len += 1;
+            }
+            boundary => {
+                if segment_len == 0 {
+                    if accumulated_len == 0 && boundary != Some(&b'.') {
+                        return Ok(("", input));
+                    } else {
+                        return Err(Error::new(ErrorKind::EmptySegment(pos)));
+                    }
+                }
+                if pos == Position::Pre
+                    && segment_len > 1
+                    && !segment_has_nondigit
+                    && input[accumulated_len..].starts_with('0')
+                {
+                    return Err(Error::new(ErrorKind::LeadingZero(pos)));
+                }
+                accumulated_len += segment_len;
+                if boundary == Some(&b'.') {
+                    accumulated_len += 1;
+                    segment_len = 0;
+                    segment_has_nondigit = false;
+                } else {
+                    return Ok(input.split_at(accumulated_len));
+                }
+            }
+        }
+    }
+}
+
+fn op(input: &str) -> (Op, &str) {
+    let bytes = input.as_bytes();
+    if bytes.get(0) == Some(&b'=') {
+        (Op::Exact, &input[1..])
+    } else if bytes.get(0) == Some(&b'>') {
+        if bytes.get(1) == Some(&b'=') {
+            (Op::GreaterEq, &input[2..])
+        } else {
+            (Op::Greater, &input[1..])
+        }
+    } else if bytes.get(0) == Some(&b'<') {
+        if bytes.get(1) == Some(&b'=') {
+            (Op::LessEq, &input[2..])
+        } else {
+            (Op::Less, &input[1..])
+        }
+    } else if bytes.get(0) == Some(&b'~') {
+        (Op::Tilde, &input[1..])
+    } else if bytes.get(0) == Some(&b'^') {
+        (Op::Caret, &input[1..])
+    } else {
+        (Op::DEFAULT, input)
+    }
+}
+
+fn comparator(input: &str) -> Result<(Comparator, Position, &str), Error> {
+    let (mut op, text) = op(input);
+    let default_op = input.len() == text.len();
+    let text = text.trim_start_matches(' ');
+
+    let mut pos = Position::Major;
+    let (major, text) = numeric_identifier(text, pos)?;
+    let mut has_wildcard = false;
+
+    let (minor, text) = if let Some(text) = text.strip_prefix('.') {
+        pos = Position::Minor;
+        if let Some(text) = wildcard(text) {
+            has_wildcard = true;
+            if default_op {
+                op = Op::Wildcard;
+            }
+            (None, text)
+        } else {
+            let (minor, text) = numeric_identifier(text, pos)?;
+            (Some(minor), text)
+        }
+    } else {
+        (None, text)
+    };
+
+    let (patch, text) = if let Some(text) = text.strip_prefix('.') {
+        pos = Position::Patch;
+        if let Some(text) = wildcard(text) {
+            if default_op {
+                op = Op::Wildcard;
+            }
+            (None, text)
+        } else if has_wildcard {
+            return Err(Error::new(ErrorKind::UnexpectedAfterWildcard));
+        } else {
+            let (patch, text) = numeric_identifier(text, pos)?;
+            (Some(patch), text)
+        }
+    } else {
+        (None, text)
+    };
+
+    let (pre, text) = if patch.is_some() && text.starts_with('-') {
+        pos = Position::Pre;
+        let text = &text[1..];
+        let (pre, text) = prerelease_identifier(text)?;
+        if pre.is_empty() {
+            return Err(Error::new(ErrorKind::EmptySegment(pos)));
+        }
+        (pre, text)
+    } else {
+        (Prerelease::EMPTY, text)
+    };
+
+    let text = if patch.is_some() && text.starts_with('+') {
+        pos = Position::Build;
+        let text = &text[1..];
+        let (build, text) = build_identifier(text)?;
+        if build.is_empty() {
+            return Err(Error::new(ErrorKind::EmptySegment(pos)));
+        }
+        text
+    } else {
+        text
+    };
+
+    let text = text.trim_start_matches(' ');
+
+    let comparator = Comparator {
+        op,
+        major,
+        minor,
+        patch,
+        pre,
+    };
+
+    Ok((comparator, pos, text))
+}
+
+fn version_req(input: &str, out: &mut Vec<Comparator>, depth: usize) -> Result<usize, Error> {
+    let (comparator, pos, text) = comparator(input)?;
+
+    if text.is_empty() {
+        out.reserve_exact(depth + 1);
+        unsafe { out.as_mut_ptr().add(depth).write(comparator) }
+        return Ok(depth + 1);
+    }
+
+    let text = if let Some(text) = text.strip_prefix(',') {
+        text.trim_start_matches(' ')
+    } else {
+        let unexpected = text.chars().next().unwrap();
+        return Err(Error::new(ErrorKind::ExpectedCommaFound(pos, unexpected)));
+    };
+
+    const MAX_COMPARATORS: usize = 32;
+    if depth + 1 == MAX_COMPARATORS {
+        return Err(Error::new(ErrorKind::ExcessiveComparators));
+    }
+
+    // Recurse to collect parsed Comparator objects on the stack. We perform a
+    // single allocation to allocate exactly the right sized Vec only once the
+    // total number of comparators is known.
+    let len = version_req(text, out, depth + 1)?;
+    unsafe { out.as_mut_ptr().add(depth).write(comparator) }
+    Ok(len)
+}
diff --git a/third_party/rust/semver/v1/crate/src/serde.rs b/third_party/rust/semver/v1/crate/src/serde.rs
new file mode 100644
index 0000000..06eceb6
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/src/serde.rs
@@ -0,0 +1,74 @@
+use crate::{Version, VersionReq};
+use core::fmt;
+use serde::de::{Deserialize, Deserializer, Error, Visitor};
+use serde::ser::{Serialize, Serializer};
+
+impl Serialize for Version {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.collect_str(self)
+    }
+}
+
+impl Serialize for VersionReq {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.collect_str(self)
+    }
+}
+
+impl<'de> Deserialize<'de> for Version {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct VersionVisitor;
+
+        impl<'de> Visitor<'de> for VersionVisitor {
+            type Value = Version;
+
+            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                formatter.write_str("semver version")
+            }
+
+            fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
+            where
+                E: Error,
+            {
+                string.parse().map_err(Error::custom)
+            }
+        }
+
+        deserializer.deserialize_str(VersionVisitor)
+    }
+}
+
+impl<'de> Deserialize<'de> for VersionReq {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct VersionReqVisitor;
+
+        impl<'de> Visitor<'de> for VersionReqVisitor {
+            type Value = VersionReq;
+
+            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                formatter.write_str("semver version")
+            }
+
+            fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
+            where
+                E: Error,
+            {
+                string.parse().map_err(Error::custom)
+            }
+        }
+
+        deserializer.deserialize_str(VersionReqVisitor)
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/tests/node/mod.rs b/third_party/rust/semver/v1/crate/tests/node/mod.rs
new file mode 100644
index 0000000..9750a31
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/tests/node/mod.rs
@@ -0,0 +1,43 @@
+#![cfg(test_node_semver)]
+
+use semver::Version;
+use std::fmt::{self, Display};
+use std::process::Command;
+
+#[derive(Eq, PartialEq, Hash, Debug)]
+pub(super) struct VersionReq(semver::VersionReq);
+
+impl VersionReq {
+    pub(super) const STAR: Self = VersionReq(semver::VersionReq::STAR);
+
+    pub(super) fn matches(&self, version: &Version) -> bool {
+        let out = Command::new("node")
+            .arg("-e")
+            .arg(format!(
+                "console.log(require('semver').satisfies('{}', '{}'))",
+                version,
+                self.to_string().replace(',', ""),
+            ))
+            .output()
+            .unwrap();
+        if out.stdout == b"true\n" {
+            true
+        } else if out.stdout == b"false\n" {
+            false
+        } else {
+            let s = String::from_utf8_lossy(&out.stdout) + String::from_utf8_lossy(&out.stderr);
+            panic!("unexpected output: {}", s);
+        }
+    }
+}
+
+impl Display for VersionReq {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(&self.0, formatter)
+    }
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn req(text: &str) -> VersionReq {
+    VersionReq(crate::util::req(text))
+}
diff --git a/third_party/rust/semver/v1/crate/tests/test_identifier.rs b/third_party/rust/semver/v1/crate/tests/test_identifier.rs
new file mode 100644
index 0000000..65c9ec5
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/tests/test_identifier.rs
@@ -0,0 +1,38 @@
+mod util;
+
+use crate::util::*;
+use semver::Prerelease;
+
+#[test]
+fn test_new() {
+    fn test(identifier: Prerelease, expected: &str) {
+        assert_eq!(identifier.is_empty(), expected.is_empty());
+        assert_eq!(identifier.len(), expected.len());
+        assert_eq!(identifier.as_str(), expected);
+        assert_eq!(identifier, identifier);
+        assert_eq!(identifier, identifier.clone());
+    }
+
+    let ref mut string = String::new();
+    let limit = if cfg!(miri) { 40 } else { 280 }; // miri is slow
+    for _ in 0..limit {
+        test(prerelease(string), string);
+        string.push('1');
+    }
+
+    if !cfg!(miri) {
+        let ref string = string.repeat(20000);
+        test(prerelease(string), string);
+    }
+}
+
+#[test]
+fn test_eq() {
+    assert_eq!(prerelease("-"), prerelease("-"));
+    assert_ne!(prerelease("a"), prerelease("aa"));
+    assert_ne!(prerelease("aa"), prerelease("a"));
+    assert_ne!(prerelease("aaaaaaaaa"), prerelease("a"));
+    assert_ne!(prerelease("a"), prerelease("aaaaaaaaa"));
+    assert_ne!(prerelease("aaaaaaaaa"), prerelease("bbbbbbbbb"));
+    assert_ne!(build_metadata("1"), build_metadata("001"));
+}
diff --git a/third_party/rust/semver/v1/crate/tests/test_version.rs b/third_party/rust/semver/v1/crate/tests/test_version.rs
new file mode 100644
index 0000000..c262be79
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/tests/test_version.rs
@@ -0,0 +1,235 @@
+mod util;
+
+use crate::util::*;
+use semver::{BuildMetadata, Prerelease, Version};
+
+#[test]
+fn test_parse() {
+    let err = version_err("");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing major version number",
+    );
+
+    let err = version_err("  ");
+    assert_to_string(
+        err,
+        "unexpected character ' ' while parsing major version number",
+    );
+
+    let err = version_err("1");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing major version number",
+    );
+
+    let err = version_err("1.2");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing minor version number",
+    );
+
+    let err = version_err("1.2.3-");
+    assert_to_string(err, "empty identifier segment in pre-release identifier");
+
+    let err = version_err("a.b.c");
+    assert_to_string(
+        err,
+        "unexpected character 'a' while parsing major version number",
+    );
+
+    let err = version_err("1.2.3 abc");
+    assert_to_string(err, "unexpected character ' ' after patch version number");
+
+    let err = version_err("1.2.3-01");
+    assert_to_string(err, "invalid leading zero in pre-release identifier");
+
+    let parsed = version("1.2.3");
+    let expected = Version::new(1, 2, 3);
+    assert_eq!(parsed, expected);
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: Prerelease::EMPTY,
+        build: BuildMetadata::EMPTY,
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3-alpha1");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: prerelease("alpha1"),
+        build: BuildMetadata::EMPTY,
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3+build5");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: Prerelease::EMPTY,
+        build: build_metadata("build5"),
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3+5build");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: Prerelease::EMPTY,
+        build: build_metadata("5build"),
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3-alpha1+build5");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: prerelease("alpha1"),
+        build: build_metadata("build5"),
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3-1.alpha1.9+build5.7.3aedf");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: prerelease("1.alpha1.9"),
+        build: build_metadata("build5.7.3aedf"),
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("1.2.3-0a.alpha1.9+05build.7.3aedf");
+    let expected = Version {
+        major: 1,
+        minor: 2,
+        patch: 3,
+        pre: prerelease("0a.alpha1.9"),
+        build: build_metadata("05build.7.3aedf"),
+    };
+    assert_eq!(parsed, expected);
+
+    let parsed = version("0.4.0-beta.1+0851523");
+    let expected = Version {
+        major: 0,
+        minor: 4,
+        patch: 0,
+        pre: prerelease("beta.1"),
+        build: build_metadata("0851523"),
+    };
+    assert_eq!(parsed, expected);
+
+    // for https://nodejs.org/dist/index.json, where some older npm versions are "1.1.0-beta-10"
+    let parsed = version("1.1.0-beta-10");
+    let expected = Version {
+        major: 1,
+        minor: 1,
+        patch: 0,
+        pre: prerelease("beta-10"),
+        build: BuildMetadata::EMPTY,
+    };
+    assert_eq!(parsed, expected);
+}
+
+#[test]
+fn test_eq() {
+    assert_eq!(version("1.2.3"), version("1.2.3"));
+    assert_eq!(version("1.2.3-alpha1"), version("1.2.3-alpha1"));
+    assert_eq!(version("1.2.3+build.42"), version("1.2.3+build.42"));
+    assert_eq!(version("1.2.3-alpha1+42"), version("1.2.3-alpha1+42"));
+}
+
+#[test]
+fn test_ne() {
+    assert_ne!(version("0.0.0"), version("0.0.1"));
+    assert_ne!(version("0.0.0"), version("0.1.0"));
+    assert_ne!(version("0.0.0"), version("1.0.0"));
+    assert_ne!(version("1.2.3-alpha"), version("1.2.3-beta"));
+    assert_ne!(version("1.2.3+23"), version("1.2.3+42"));
+}
+
+#[test]
+fn test_display() {
+    assert_to_string(version("1.2.3"), "1.2.3");
+    assert_to_string(version("1.2.3-alpha1"), "1.2.3-alpha1");
+    assert_to_string(version("1.2.3+build.42"), "1.2.3+build.42");
+    assert_to_string(version("1.2.3-alpha1+42"), "1.2.3-alpha1+42");
+}
+
+#[test]
+fn test_lt() {
+    assert!(version("0.0.0") < version("1.2.3-alpha2"));
+    assert!(version("1.0.0") < version("1.2.3-alpha2"));
+    assert!(version("1.2.0") < version("1.2.3-alpha2"));
+    assert!(version("1.2.3-alpha1") < version("1.2.3"));
+    assert!(version("1.2.3-alpha1") < version("1.2.3-alpha2"));
+    assert!(!(version("1.2.3-alpha2") < version("1.2.3-alpha2")));
+    assert!(version("1.2.3+23") < version("1.2.3+42"));
+}
+
+#[test]
+fn test_le() {
+    assert!(version("0.0.0") <= version("1.2.3-alpha2"));
+    assert!(version("1.0.0") <= version("1.2.3-alpha2"));
+    assert!(version("1.2.0") <= version("1.2.3-alpha2"));
+    assert!(version("1.2.3-alpha1") <= version("1.2.3-alpha2"));
+    assert!(version("1.2.3-alpha2") <= version("1.2.3-alpha2"));
+    assert!(version("1.2.3+23") <= version("1.2.3+42"));
+}
+
+#[test]
+fn test_gt() {
+    assert!(version("1.2.3-alpha2") > version("0.0.0"));
+    assert!(version("1.2.3-alpha2") > version("1.0.0"));
+    assert!(version("1.2.3-alpha2") > version("1.2.0"));
+    assert!(version("1.2.3-alpha2") > version("1.2.3-alpha1"));
+    assert!(version("1.2.3") > version("1.2.3-alpha2"));
+    assert!(!(version("1.2.3-alpha2") > version("1.2.3-alpha2")));
+    assert!(!(version("1.2.3+23") > version("1.2.3+42")));
+}
+
+#[test]
+fn test_ge() {
+    assert!(version("1.2.3-alpha2") >= version("0.0.0"));
+    assert!(version("1.2.3-alpha2") >= version("1.0.0"));
+    assert!(version("1.2.3-alpha2") >= version("1.2.0"));
+    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha1"));
+    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha2"));
+    assert!(!(version("1.2.3+23") >= version("1.2.3+42")));
+}
+
+#[test]
+fn test_spec_order() {
+    let vs = [
+        "1.0.0-alpha",
+        "1.0.0-alpha.1",
+        "1.0.0-alpha.beta",
+        "1.0.0-beta",
+        "1.0.0-beta.2",
+        "1.0.0-beta.11",
+        "1.0.0-rc.1",
+        "1.0.0",
+    ];
+    let mut i = 1;
+    while i < vs.len() {
+        let a = version(vs[i - 1]);
+        let b = version(vs[i]);
+        assert!(a < b, "nope {:?} < {:?}", a, b);
+        i += 1;
+    }
+}
+
+#[test]
+fn test_align() {
+    let version = version("1.2.3-rc1");
+    assert_eq!("1.2.3-rc1           ", format!("{:20}", version));
+    assert_eq!("*****1.2.3-rc1******", format!("{:*^20}", version));
+    assert_eq!("           1.2.3-rc1", format!("{:>20}", version));
+}
diff --git a/third_party/rust/semver/v1/crate/tests/test_version_req.rs b/third_party/rust/semver/v1/crate/tests/test_version_req.rs
new file mode 100644
index 0000000..982fcd6b
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/tests/test_version_req.rs
@@ -0,0 +1,408 @@
+mod node;
+mod util;
+
+use crate::util::*;
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
+
+#[cfg(test_node_semver)]
+use node::{req, VersionReq};
+#[cfg(not(test_node_semver))]
+use semver::VersionReq;
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+fn assert_match_all(req: &VersionReq, versions: &[&str]) {
+    for string in versions {
+        let parsed = version(string);
+        assert!(req.matches(&parsed), "did not match {}", string);
+    }
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+fn assert_match_none(req: &VersionReq, versions: &[&str]) {
+    for string in versions {
+        let parsed = version(string);
+        assert!(!req.matches(&parsed), "matched {}", string);
+    }
+}
+
+#[test]
+fn test_default() {
+    let ref r = req("1.0.0");
+    assert_to_string(r, "^1.0.0");
+    assert_match_all(r, &["1.0.0", "1.1.0", "1.0.1"]);
+    assert_match_none(r, &["0.9.9", "0.10.0", "0.1.0", "1.0.0-pre", "1.0.1-pre"]);
+}
+
+#[test]
+fn test_exact() {
+    let ref r = req("=1.0.0");
+    assert_to_string(r, "=1.0.0");
+    assert_match_all(r, &["1.0.0"]);
+    assert_match_none(r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]);
+
+    let ref r = req("=0.9.0");
+    assert_to_string(r, "=0.9.0");
+    assert_match_all(r, &["0.9.0"]);
+    assert_match_none(r, &["0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]);
+
+    let ref r = req("=0.0.2");
+    assert_to_string(r, "=0.0.2");
+    assert_match_all(r, &["0.0.2"]);
+    assert_match_none(r, &["0.0.1", "0.0.3", "0.0.2-pre"]);
+
+    let ref r = req("=0.1.0-beta2.a");
+    assert_to_string(r, "=0.1.0-beta2.a");
+    assert_match_all(r, &["0.1.0-beta2.a"]);
+    assert_match_none(r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]);
+
+    let ref r = req("=0.1.0+meta");
+    assert_to_string(r, "=0.1.0");
+    assert_match_all(r, &["0.1.0", "0.1.0+meta", "0.1.0+any"]);
+}
+
+#[test]
+pub fn test_greater_than() {
+    let ref r = req(">= 1.0.0");
+    assert_to_string(r, ">=1.0.0");
+    assert_match_all(r, &["1.0.0", "2.0.0"]);
+    assert_match_none(r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]);
+
+    let ref r = req(">= 2.1.0-alpha2");
+    assert_to_string(r, ">=2.1.0-alpha2");
+    assert_match_all(r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]);
+    assert_match_none(
+        r,
+        &["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"],
+    );
+}
+
+#[test]
+pub fn test_less_than() {
+    let ref r = req("< 1.0.0");
+    assert_to_string(r, "<1.0.0");
+    assert_match_all(r, &["0.1.0", "0.0.1"]);
+    assert_match_none(r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]);
+
+    let ref r = req("<= 2.1.0-alpha2");
+    assert_match_all(r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]);
+    assert_match_none(
+        r,
+        &["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"],
+    );
+
+    let ref r = req(">1.0.0-alpha, <1.0.0");
+    assert_match_all(r, &["1.0.0-beta"]);
+
+    let ref r = req(">1.0.0-alpha, <1.0");
+    assert_match_none(r, &["1.0.0-beta"]);
+
+    let ref r = req(">1.0.0-alpha, <1");
+    assert_match_none(r, &["1.0.0-beta"]);
+}
+
+#[test]
+pub fn test_multiple() {
+    let ref r = req("> 0.0.9, <= 2.5.3");
+    assert_to_string(r, ">0.0.9, <=2.5.3");
+    assert_match_all(r, &["0.0.10", "1.0.0", "2.5.3"]);
+    assert_match_none(r, &["0.0.8", "2.5.4"]);
+
+    let ref r = req("0.3.0, 0.4.0");
+    assert_to_string(r, "^0.3.0, ^0.4.0");
+    assert_match_none(r, &["0.0.8", "0.3.0", "0.4.0"]);
+
+    let ref r = req("<= 0.2.0, >= 0.5.0");
+    assert_to_string(r, "<=0.2.0, >=0.5.0");
+    assert_match_none(r, &["0.0.8", "0.3.0", "0.5.1"]);
+
+    let ref r = req("0.1.0, 0.1.4, 0.1.6");
+    assert_to_string(r, "^0.1.0, ^0.1.4, ^0.1.6");
+    assert_match_all(r, &["0.1.6", "0.1.9"]);
+    assert_match_none(r, &["0.1.0", "0.1.4", "0.2.0"]);
+
+    let err = req_err("> 0.1.0,");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing major version number",
+    );
+
+    let err = req_err("> 0.3.0, ,");
+    assert_to_string(
+        err,
+        "unexpected character ',' while parsing major version number",
+    );
+
+    let ref r = req(">=0.5.1-alpha3, <0.6");
+    assert_to_string(r, ">=0.5.1-alpha3, <0.6");
+    assert_match_all(
+        r,
+        &[
+            "0.5.1-alpha3",
+            "0.5.1-alpha4",
+            "0.5.1-beta",
+            "0.5.1",
+            "0.5.5",
+        ],
+    );
+    assert_match_none(
+        r,
+        &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],
+    );
+    assert_match_none(r, &["0.6.0", "0.6.0-pre"]);
+
+    // https://github.com/steveklabnik/semver/issues/56
+    let err = req_err("1.2.3 - 2.3.4");
+    assert_to_string(err, "expected comma after patch version number, found '-'");
+}
+
+#[test]
+pub fn test_whitespace_delimited_comparator_sets() {
+    // https://github.com/steveklabnik/semver/issues/55
+    let err = req_err("> 0.0.9 <= 2.5.3");
+    assert_to_string(err, "expected comma after patch version number, found '<'");
+}
+
+#[test]
+pub fn test_tilde() {
+    let ref r = req("~1");
+    assert_match_all(r, &["1.0.0", "1.0.1", "1.1.1"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "0.0.9"]);
+
+    let ref r = req("~1.2");
+    assert_match_all(r, &["1.2.0", "1.2.1"]);
+    assert_match_none(r, &["1.1.1", "1.3.0", "0.0.9"]);
+
+    let ref r = req("~1.2.2");
+    assert_match_all(r, &["1.2.2", "1.2.4"]);
+    assert_match_none(r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
+
+    let ref r = req("~1.2.3-beta.2");
+    assert_match_all(r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]);
+    assert_match_none(r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]);
+}
+
+#[test]
+pub fn test_caret() {
+    let ref r = req("^1");
+    assert_match_all(r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "0.1.4"]);
+    assert_match_none(r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]);
+
+    let ref r = req("^1.1");
+    assert_match_all(r, &["1.1.2", "1.1.0", "1.2.1"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]);
+
+    let ref r = req("^1.1.2");
+    assert_match_all(r, &["1.1.2", "1.1.4", "1.2.1"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
+    assert_match_none(r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]);
+
+    let ref r = req("^0.1.2");
+    assert_match_all(r, &["0.1.2", "0.1.4"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
+    assert_match_none(r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]);
+
+    let ref r = req("^0.5.1-alpha3");
+    assert_match_all(
+        r,
+        &[
+            "0.5.1-alpha3",
+            "0.5.1-alpha4",
+            "0.5.1-beta",
+            "0.5.1",
+            "0.5.5",
+        ],
+    );
+    assert_match_none(
+        r,
+        &[
+            "0.5.1-alpha1",
+            "0.5.2-alpha3",
+            "0.5.5-pre",
+            "0.5.0-pre",
+            "0.6.0",
+        ],
+    );
+
+    let ref r = req("^0.0.2");
+    assert_match_all(r, &["0.0.2"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]);
+
+    let ref r = req("^0.0");
+    assert_match_all(r, &["0.0.2", "0.0.0"]);
+    assert_match_none(r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]);
+
+    let ref r = req("^0");
+    assert_match_all(r, &["0.9.1", "0.0.2", "0.0.0"]);
+    assert_match_none(r, &["2.9.0", "1.1.1"]);
+
+    let ref r = req("^1.4.2-beta.5");
+    assert_match_all(
+        r,
+        &["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"],
+    );
+    assert_match_none(
+        r,
+        &[
+            "0.9.9",
+            "2.0.0",
+            "1.4.2-alpha",
+            "1.4.2-beta.4",
+            "1.4.3-beta.5",
+        ],
+    );
+}
+
+#[test]
+pub fn test_wildcard() {
+    let err = req_err("");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing major version number",
+    );
+
+    let ref r = req("*");
+    assert_match_all(r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
+    assert_match_none(r, &["1.0.0-pre"]);
+
+    for s in &["x", "X"] {
+        assert_eq!(*r, req(s));
+    }
+
+    let ref r = req("1.*");
+    assert_match_all(r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
+    assert_match_none(r, &["0.0.9", "1.2.0-pre"]);
+
+    for s in &["1.x", "1.X", "1.*.*"] {
+        assert_eq!(*r, req(s));
+    }
+
+    let ref r = req("1.2.*");
+    assert_match_all(r, &["1.2.0", "1.2.2", "1.2.4"]);
+    assert_match_none(r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3", "1.2.2-pre"]);
+
+    for s in &["1.2.x", "1.2.X"] {
+        assert_eq!(*r, req(s));
+    }
+}
+
+#[test]
+pub fn test_logical_or() {
+    // https://github.com/steveklabnik/semver/issues/57
+    let err = req_err("=1.2.3 || =2.3.4");
+    assert_to_string(err, "expected comma after patch version number, found '|'");
+
+    let err = req_err("1.1 || =1.2.3");
+    assert_to_string(err, "expected comma after minor version number, found '|'");
+
+    let err = req_err("6.* || 8.* || >= 10.*");
+    assert_to_string(err, "expected comma after minor version number, found '|'");
+}
+
+#[test]
+pub fn test_any() {
+    #[cfg(not(no_const_vec_new))]
+    let ref r = VersionReq::STAR;
+    #[cfg(no_const_vec_new)]
+    let ref r = VersionReq {
+        comparators: Vec::new(),
+    };
+    assert_match_all(r, &["0.0.1", "0.1.0", "1.0.0"]);
+}
+
+#[test]
+pub fn test_pre() {
+    let ref r = req("=2.1.1-really.0");
+    assert_match_all(r, &["2.1.1-really.0"]);
+}
+
+#[test]
+pub fn test_parse_errors() {
+    let err = req_err("\0");
+    assert_to_string(
+        err,
+        "unexpected character '\\u{0}' while parsing major version number",
+    );
+
+    let err = req_err(">= >= 0.0.2");
+    assert_to_string(
+        err,
+        "unexpected character '>' while parsing major version number",
+    );
+
+    let err = req_err(">== 0.0.2");
+    assert_to_string(
+        err,
+        "unexpected character '=' while parsing major version number",
+    );
+
+    let err = req_err("a.0.0");
+    assert_to_string(
+        err,
+        "unexpected character 'a' while parsing major version number",
+    );
+
+    let err = req_err("1.0.0-");
+    assert_to_string(err, "empty identifier segment in pre-release identifier");
+
+    let err = req_err(">=");
+    assert_to_string(
+        err,
+        "unexpected end of input while parsing major version number",
+    );
+}
+
+#[test]
+fn test_cargo3202() {
+    let ref r = req("0.*.*");
+    assert_to_string(r, "0.*");
+    assert_match_all(r, &["0.5.0"]);
+
+    let ref r = req("0.0.*");
+    assert_to_string(r, "0.0.*");
+}
+
+#[test]
+fn test_digit_after_wildcard() {
+    let err = req_err("*.1");
+    assert_to_string(err, "unexpected character after wildcard in version req");
+
+    let err = req_err("1.*.1");
+    assert_to_string(err, "unexpected character after wildcard in version req");
+
+    let err = req_err(">=1.*.1");
+    assert_to_string(err, "unexpected character after wildcard in version req");
+}
+
+#[test]
+fn test_eq_hash() {
+    fn calculate_hash(value: impl Hash) -> u64 {
+        let mut hasher = DefaultHasher::new();
+        value.hash(&mut hasher);
+        hasher.finish()
+    }
+
+    assert!(req("^1") == req("^1"));
+    assert!(calculate_hash(req("^1")) == calculate_hash(req("^1")));
+    assert!(req("^1") != req("^2"));
+}
+
+#[test]
+fn test_parsing_pre_and_build_metadata_see_issue_217() {
+    for op in &["=", ">", ">=", "<", "<=", "~", "^"] {
+        // digit then alpha
+        req(&format!("{} 1.2.3-1a", op));
+        req(&format!("{} 1.2.3+1a", op));
+
+        // digit then alpha (leading zero)
+        req(&format!("{} 1.2.3-01a", op));
+        req(&format!("{} 1.2.3+01", op));
+
+        // multiple
+        req(&format!("{} 1.2.3-1+1", op));
+        req(&format!("{} 1.2.3-1-1+1-1-1", op));
+        req(&format!("{} 1.2.3-1a+1a", op));
+        req(&format!("{} 1.2.3-1a-1a+1a-1a-1a", op));
+    }
+}
diff --git a/third_party/rust/semver/v1/crate/tests/util/mod.rs b/third_party/rust/semver/v1/crate/tests/util/mod.rs
new file mode 100644
index 0000000..5cc142c
--- /dev/null
+++ b/third_party/rust/semver/v1/crate/tests/util/mod.rs
@@ -0,0 +1,39 @@
+#![allow(dead_code)]
+
+use semver::{BuildMetadata, Error, Prerelease, Version, VersionReq};
+use std::fmt::Display;
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn version(text: &str) -> Version {
+    Version::parse(text).unwrap()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn version_err(text: &str) -> Error {
+    Version::parse(text).unwrap_err()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn req(text: &str) -> VersionReq {
+    VersionReq::parse(text).unwrap()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn req_err(text: &str) -> Error {
+    VersionReq::parse(text).unwrap_err()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn prerelease(text: &str) -> Prerelease {
+    Prerelease::new(text).unwrap()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn build_metadata(text: &str) -> BuildMetadata {
+    BuildMetadata::new(text).unwrap()
+}
+
+#[cfg_attr(not(no_track_caller), track_caller)]
+pub(super) fn assert_to_string(value: impl Display, expected: &str) {
+    assert_eq!(value.to_string(), expected);
+}
diff --git a/third_party/rust/syn/v1/BUILD.gn b/third_party/rust/syn/v1/BUILD.gn
index 19963add..528748d 100644
--- a/third_party/rust/syn/v1/BUILD.gn
+++ b/third_party/rust/syn/v1/BUILD.gn
@@ -26,11 +26,14 @@
   features = [
     "clone-impls",
     "derive",
+    "extra-traits",
     "full",
     "parsing",
     "printing",
     "proc-macro",
     "quote",
+    "visit",
+    "visit-mut",
   ]
   build_root = "crate/build.rs"
   build_sources = [ "crate/build.rs" ]
diff --git a/third_party/rust/third_party.toml b/third_party/rust/third_party.toml
index be98d14..910c8a6 100644
--- a/third_party/rust/third_party.toml
+++ b/third_party/rust/third_party.toml
@@ -55,3 +55,4 @@
 # This allows them built in a configuration that can be used from Chromium
 # tests, but not used from production.
 [dev-dependencies]
+rstest = "0.12"
diff --git a/tools/binary_size/libsupersize/README.md b/tools/binary_size/libsupersize/README.md
index 87c05b4..71fe1bc5 100644
--- a/tools/binary_size/libsupersize/README.md
+++ b/tools/binary_size/libsupersize/README.md
@@ -2,8 +2,8 @@
 
 SuperSize is comprised of two parts:
 
-1. A command-line tool for creating and inspecting `.size` files,
-2. A web app for visualizing `.size` files.
+1. A command-line tool for creating and inspecting `.size` and `.sizediff` files,
+2. A web app for visualizing `.size` and `.sizediff` files.
 
 For more details, see [//tools/binary_size/libsupersize/docs].
 
@@ -47,7 +47,7 @@
 
 Example Usage:
 
-``` bash
+```bash
 # Android:
 autoninja -C out/Release apks/ChromePublic.apk
 tools/binary_size/supersize archive chrome.size -f out/Release/apks/ChromePublic.apk -v
@@ -74,7 +74,7 @@
 
 Example Session:
 
-``` python
+```python
 >>> ShowExamples()  # Get some inspiration.
 ...
 >>> sorted = size_info.symbols.WhereInSection('t').Sorted()
@@ -94,6 +94,17 @@
 >>> Print((t[1] for t in ReadStringLiterals(syms)), to_file='strings.txt')
 ```
 
+### supersize save_diff
+
+Creates a `.sizediff` file given two `.size` files. A `.sizediff` file contains
+two `.size` files, with all unchanged symbols removed.
+
+Example Usage:
+
+```bash
+tools/binary_size/supersize size_diff before.size after.size out.sizediff
+```
+
 ### supersize diff
 
 A convenience command equivalent to:
@@ -101,6 +112,6 @@
 
 Example Usage:
 
-``` bash
+```bash
 tools/binary_size/supersize diff before.size after.size --all
 ```
diff --git a/tools/binary_size/libsupersize/apk.py b/tools/binary_size/libsupersize/apk.py
index 7a5e990..d77e60a4 100644
--- a/tools/binary_size/libsupersize/apk.py
+++ b/tools/binary_size/libsupersize/apk.py
@@ -10,6 +10,7 @@
 import zipfile
 
 import archive_util
+import file_format
 import models
 import zip_util
 
@@ -89,7 +90,7 @@
 
 
 def CreateApkOtherSymbols(*, metadata, apk_spec, native_spec):
-  """Creates a Container (with sections sizes) and symbols for a SizeInfo.
+  """Creates symbols for resources / assets within the apk.
 
   Args:
     metadata: Metadata dict from CreateMetadata().
@@ -160,4 +161,5 @@
   section_ranges = {}
   archive_util.ExtendSectionRange(section_ranges, models.SECTION_OTHER,
                                   sum(s.size for s in raw_symbols))
+  file_format.SortSymbols(raw_symbols)
   return section_ranges, raw_symbols
diff --git a/tools/binary_size/libsupersize/apkanalyzer.py b/tools/binary_size/libsupersize/apkanalyzer.py
index c83b8a2..b57e5a9 100644
--- a/tools/binary_size/libsupersize/apkanalyzer.py
+++ b/tools/binary_size/libsupersize/apkanalyzer.py
@@ -34,6 +34,15 @@
 
 
 def RunApkAnalyzerAsync(apk_path, mapping_path):
+  """Starts an apkanalyzer job for the given apk.
+
+  Args:
+    apk_path: Path to the apk to run on.
+    mapping_path: Path to the proguard mapping file.
+
+  Returns:
+    An object to pass to CreateDexSymbols().
+  """
   args = [path_util.GetApkAnalyzerPath(), 'dex', 'packages', apk_path]
   if mapping_path and os.path.exists(mapping_path):
     args.extend(['--proguard-mappings', mapping_path])
@@ -278,7 +287,35 @@
                        source_path=source_path)
 
 
-def CreateDexSymbols(apk_analyzer_result, dex_total_size, size_info_prefix):
+def _SymbolsFromNodes(nodes, source_map):
+  # Use (DEX_METHODS, DEX) buckets to speed up sorting.
+  symbol_buckets = ([], [])
+  lambda_normalizer = LambdaNormalizer()
+  for _, name, node_size in nodes:
+    symbol = CreateDexSymbol(name, node_size, source_map, lambda_normalizer)
+    if symbol:
+      bucket_index = int(symbol.section_name is models.SECTION_DEX)
+      symbol_buckets[bucket_index].append(symbol)
+  for symbols_bucket in symbol_buckets:
+    symbols_bucket.sort(key=lambda s: s.full_name)
+  return symbol_buckets
+
+
+def CreateDexSymbols(apk_analyzer_async_result, dex_total_size,
+                     size_info_prefix):
+  """Creates DEX symbols given apk_analyzer output.
+
+  Args:
+    apk_analyzer_async_result: Return value from RunApkAnalyzerAsync().
+    dex_total_size: Sum of the sizes of all .dex files in the apk.
+    size_info_prefix: Path such as: out/Release/size-info/BaseName.
+
+  Returns:
+    A tuple of (section_ranges, raw_symbols).
+  """
+  logging.debug('Waiting for apkanalyzer to finish')
+  apk_analyzer_result = apk_analyzer_async_result.get()
+  logging.debug('Analyzing DEX - processing results')
   source_map = _ParseJarInfoFile(size_info_prefix + '.jar.info')
 
   nodes = _ParseApkAnalyzerOutput(apk_analyzer_result.stdout,
@@ -293,15 +330,32 @@
     logging.error(
         'Node size too large, check for node processing errors. '
         'dex_total_size=%d total_node_size=%d', dex_total_size, total_node_size)
-  # Use (DEX_METHODS, DEX) buckets to speed up sorting.
-  symbols = ([], [])
-  lambda_normalizer = LambdaNormalizer()
-  for _, name, node_size in nodes:
-    symbol = CreateDexSymbol(name, node_size, source_map, lambda_normalizer)
-    if symbol:
-      symbols[int(symbol.section_name is models.SECTION_DEX)].append(symbol)
 
-  symbols[0].sort(key=lambda s: s.full_name)
-  symbols[1].sort(key=lambda s: s.full_name)
-  symbols[0].extend(symbols[1])
-  return symbols[0]
+  dex_method_symbols, dex_other_symbols = _SymbolsFromNodes(nodes, source_map)
+
+  dex_method_size = round(sum(s.pss for s in dex_method_symbols))
+  dex_other_size = round(sum(s.pss for s in dex_other_symbols))
+
+  unattributed_dex = dex_total_size - dex_method_size - dex_other_size
+  # Compare against -5 instead of 0 to guard against round-off errors.
+  assert unattributed_dex >= -5, (
+      'sum(dex_symbols.size) > filesize(classes.dex). {} vs {}'.format(
+          dex_method_size + dex_other_size, dex_total_size))
+
+  if unattributed_dex > 0:
+    dex_other_symbols.append(
+        models.Symbol(
+            models.SECTION_DEX,
+            unattributed_dex,
+            full_name='** .dex (unattributed - includes string literals)'))
+
+  # We can't meaningfully track section size of dex methods vs other, so
+  # just fake the size of dex methods as the sum of symbols, and make
+  # "dex other" responsible for any unattributed bytes.
+  section_ranges = {
+      models.SECTION_DEX_METHOD: (0, dex_method_size),
+      models.SECTION_DEX: (0, dex_total_size - dex_method_size),
+  }
+
+  dex_other_symbols.extend(dex_method_symbols)
+  return section_ranges, dex_other_symbols
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index b0d7fae..8a70b76 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -289,52 +289,6 @@
   return section_ranges, raw_symbols
 
 
-def _CreateDexSymbols(*, apk_spec, apk_infolist, apk_analyzer_result):
-  """Create dex symbols for the given apk_spec.
-
-  Returns:
-    A tuple of (section_ranges, raw_symbols).
-  """
-  logging.info('Analyzing classes.dex for %s', apk_spec.split_name
-               or apk_spec.apk_path)
-
-  dex_total_size = sum(i.file_size for i in apk_infolist
-                       if i.filename.endswith('.dex'))
-  raw_symbols = apkanalyzer.CreateDexSymbols(apk_analyzer_result,
-                                             dex_total_size,
-                                             apk_spec.size_info_prefix)
-
-  sizes = collections.Counter()
-  for s in raw_symbols:
-    sizes[s.section_name] += s.pss
-  assert len(sizes) <= 2, 'Unexpected: ' + str(sizes)
-  dex_method_size = round(sizes[models.SECTION_DEX_METHOD])
-  dex_other_size = round(sizes[models.SECTION_DEX])
-
-  unattributed_dex = dex_total_size - dex_method_size - dex_other_size
-  # Compare against -5 instead of 0 to guard against round-off errors.
-  assert unattributed_dex >= -5, (
-      'sum(dex_symbols.size) > filesize(classes.dex). {} vs {}'.format(
-          dex_method_size + dex_other_size, dex_total_size))
-
-  if unattributed_dex > 0:
-    raw_symbols.append(
-        models.Symbol(
-            models.SECTION_DEX,
-            unattributed_dex,
-            full_name='** .dex (unattributed - includes string literals)'))
-
-  # We can't meaningfully track section size of dex methods vs other, so
-  # just fake the size of dex methods as the sum of symbols, and make
-  # "dex other" responsible for any unattributed bytes.
-  section_ranges = {
-      models.SECTION_DEX_METHOD: (0, dex_method_size),
-      models.SECTION_DEX: (0, dex_total_size - dex_method_size),
-  }
-
-  return section_ranges, raw_symbols
-
-
 def _CreateContainerSymbols(container_spec, apk_file_manager,
                             apk_analyzer_results, pak_id_map):
   container_name = container_spec.container_name
@@ -403,18 +357,21 @@
              component=native_spec.component,
              paths_already_normalized=True)
 
+  if apk_spec and apk_spec.analyze_dex:
+    logging.info('Analyzing DEX')
+    apk_infolist = apk_file_manager.InfoList(apk_spec.apk_path)
+    dex_total_size = sum(i.file_size for i in apk_infolist
+                         if i.filename.endswith('.dex'))
+    add_syms(*apkanalyzer.CreateDexSymbols(apk_analyzer_results[container_name],
+                                           dex_total_size,
+                                           apk_spec.size_info_prefix))
+
   if pak_spec:
     add_syms(*_CreatePakSymbols(pak_spec=pak_spec,
                                 pak_id_map=pak_id_map,
                                 apk_spec=apk_spec,
                                 output_directory=output_directory))
   if apk_spec:
-    if apk_spec.analyze_dex:
-      apk_infolist = apk_file_manager.InfoList(apk_spec.apk_path)
-      apk_analyzer_result = apk_analyzer_results[container_name].get()
-      add_syms(*_CreateDexSymbols(apk_spec=apk_spec,
-                                  apk_infolist=apk_infolist,
-                                  apk_analyzer_result=apk_analyzer_result))
     add_syms(*apk.CreateApkOtherSymbols(
         metadata=metadata, apk_spec=apk_spec, native_spec=native_spec))
 
@@ -424,7 +381,8 @@
   for symbol in raw_symbols:
     symbol.container = container
 
-  file_format.SortSymbols(raw_symbols, check_already_mostly_sorted=True)
+  if file_format.LogUnsortedSymbols(raw_symbols) > 0:
+    file_format.SortSymbols(raw_symbols)
   return raw_symbols
 
 
diff --git a/tools/binary_size/libsupersize/file_format.py b/tools/binary_size/libsupersize/file_format.py
index 4e2184be..1bcee95 100644
--- a/tools/binary_size/libsupersize/file_format.py
+++ b/tools/binary_size/libsupersize/file_format.py
@@ -83,7 +83,62 @@
     logging.debug('File size with %s: %d' % (desc, size))
 
 
-def SortSymbols(raw_symbols, check_already_mostly_sorted=True):
+def _SortKey(s):
+  # size_without_padding so that "** symbol gap" sorts before other symbols
+  # with same address (necessary for correctness within CalculatePadding()).
+  return (
+      _SECTION_SORT_ORDER[s.section_name],
+      s.IsOverhead(),
+      s.address,
+      # Only use size_without_padding for native symbols (that have
+      # addresses) since padding-only symbols must come first for
+      # correctness.
+      # DEX also has 0-size symbols (for nested classes, not sure why)
+      # and we don't want to sort them differently since they don't have
+      # any padding either.
+      s.address and s.size_without_padding > 0,
+      s.full_name.startswith('**'),
+      s.full_name,
+      s.object_path)
+
+
+def _DescribeSymbolSortOrder(syms):
+  return ''.join('%r: %r\n' % (_SortKey(s), s) for s in syms)
+
+
+def LogUnsortedSymbols(raw_symbols):
+  """Logs the number of symbols that are not sorted.
+
+  Also logs the first few such symbols and their sort keys.
+
+  Returns:
+    The number of unsorted symbols.
+  """
+  logging.debug('Looking for out-of-order symbols (num_symbols=%d)',
+                len(raw_symbols))
+  sort_keys = [_SortKey(s) for s in raw_symbols]
+  count = sum(
+      int(sort_keys[i] > sort_keys[i + 1]) for i in range(len(raw_symbols) - 1))
+  logging.info('Out of %d symbols, %d were out-of-order.', len(raw_symbols),
+               count)
+
+  # Log them if > 1% are out-of-order and it's not a tiny sample.
+  if len(raw_symbols) > 1000 and count / len(raw_symbols) > .01:
+    NUM_TO_LOG = 10
+    logging.warning('Showing the first %d out-of-order symbols.', NUM_TO_LOG)
+    num_reported = 0
+    for i in range(len(raw_symbols) - 1):
+      if sort_keys[i] > sort_keys[i + 1]:
+        num_reported += 1
+        logging.warning('\n%d) %s\n%d) %s\n', i,
+                        _DescribeSymbolSortOrder(raw_symbols[i:i + 1]), i + 1,
+                        _DescribeSymbolSortOrder(raw_symbols[i + 1:i + 2]))
+        if num_reported == NUM_TO_LOG:
+          break
+  return count
+
+
+def SortSymbols(raw_symbols):
   """Sorts the given symbols in the order that they should be archived in.
 
   The sort order is chosen such that:
@@ -94,32 +149,7 @@
 
   Args:
     raw_symbols: List of symbols to sort.
-    check_already_mostly_sorted: Whether to assert that there are a low number
-        of out-of-order elements in raw_symbols. Older .size files are not
-        properly sorted, this check makes sense only for "supersize archive".
   """
-
-  def sort_key(s):
-    # size_without_padding so that "** symbol gap" sorts before other symbols
-    # with same address (necessary for correctness within CalculatePadding()).
-    return (
-        _SECTION_SORT_ORDER[s.section_name],
-        s.IsOverhead(),
-        s.address,
-        # Only use size_without_padding for native symbols (that have
-        # addresses) since padding-only symbols must come first for
-        # correctness.
-        # DEX also has 0-size symbols (for nested classes, not sure why)
-        # and we don't want to sort them differently since they don't have
-        # any padding either.
-        s.address and s.size_without_padding > 0,
-        s.full_name.startswith('**'),
-        s.full_name,
-        s.object_path)
-
-  def describe(syms):
-    return ''.join('%r: %r\n' % (s, sort_key(s)) for s in syms)
-
   logging.debug('Sorting %d symbols', len(raw_symbols))
 
   # Sort aliases first to make raw_symbols quicker to sort.
@@ -133,32 +163,17 @@
     if s.aliases:
       expected = raw_symbols[i:i + num_aliases]
       assert s.aliases == expected, 'Aliases out of order:\n{}\n{}'.format(
-          describe(s.aliases), describe(expected))
+          _DescribeSymbolSortOrder(s.aliases),
+          _DescribeSymbolSortOrder(expected))
 
-      s.aliases.sort(key=sort_key)
+      s.aliases.sort(key=_SortKey)
       raw_symbols[i:i + num_aliases] = s.aliases
       i += num_aliases
     else:
       i += 1
 
-  if check_already_mostly_sorted:
-    count = sum(
-        int(sort_key(raw_symbols[i]) > sort_key(raw_symbols[i + 1]))
-        for i in range(len(raw_symbols) - 1))
-    logging.debug('Number of out-of-order symbols: %d', count)
-    if count > 20:
-      logging.error('Number of out-of-order symbols: %d', count)
-      logging.error('Showing first 10')
-      num_reported = 0
-      for i in range(len(raw_symbols) - 1):
-        if sort_key(raw_symbols[i]) > sort_key(raw_symbols[i + 1]):
-          num_reported += 1
-          logging.error('\n%s', describe(raw_symbols[i:i + 2]))
-          if num_reported == 10:
-            break
-
   # Python's sort() is faster when the input list is already mostly sorted.
-  raw_symbols.sort(key=sort_key)
+  raw_symbols.sort(key=_SortKey)
 
 
 def CalculatePadding(raw_symbols):
diff --git a/tools/binary_size/libsupersize/pakfile.py b/tools/binary_size/libsupersize/pakfile.py
index 90c365c..4f575df7 100644
--- a/tools/binary_size/libsupersize/pakfile.py
+++ b/tools/binary_size/libsupersize/pakfile.py
@@ -12,6 +12,7 @@
 import zlib
 
 import archive_util
+import file_format
 import models
 import path_util
 
@@ -136,8 +137,7 @@
       raw_symbols.append(new_sym)
 
   # Pre-sort to make final sort faster.
-  # Note: _SECTION_SORT_ORDER[] for pak symbols matches section_name ordering.
-  raw_symbols.sort(key=lambda s: (s.section_name, s.address, s.object_path))
+  file_format.SortSymbols(raw_symbols)
   return raw_symbols
 
 
diff --git a/tools/binary_size/libsupersize/zip_util.py b/tools/binary_size/libsupersize/zip_util.py
index d9fdfbac1..bed4095 100644
--- a/tools/binary_size/libsupersize/zip_util.py
+++ b/tools/binary_size/libsupersize/zip_util.py
@@ -59,6 +59,7 @@
     """
     dest = self._MapPath(minimal_apks_path)
     split_names = []
+    logging.debug('Extracting %s', minimal_apks_path)
     with zipfile.ZipFile(minimal_apks_path) as z:
       for filename in z.namelist():
         # E.g.:
@@ -69,8 +70,8 @@
         m = re.match(r'splits/(.*)-master\.apk', filename)
         if m:
           split_names.append(m.group(1))
-          logging.debug('Extracting %s', filename)
           z.extract(filename, dest)
+    logging.debug('Extracting %s (done)', minimal_apks_path)
     # Make "base" comes first since that's the main chunk of work.
     # Also so that --abi-filter detection looks at it first.
     return sorted(split_names, key=lambda x: (x != 'base', x))
@@ -99,14 +100,15 @@
     The path of the temp created (and auto-deleted when context exits).
   """
   try:
+    logging.debug('Extracting %s', inner_path)
     _, suffix = os.path.splitext(inner_path)
     # Can't use NamedTemporaryFile() because it deletes via __del__, which will
     # trigger in both this and the fork()'ed processes.
     fd, temp_file = tempfile.mkstemp(suffix=suffix)
-    logging.debug('Extracting %s', inner_path)
     with zipfile.ZipFile(zip_path) as z:
       os.write(fd, z.read(inner_path))
     os.close(fd)
+    logging.debug('Extracting %s (done)', inner_path)
     yield temp_file
   finally:
     os.unlink(temp_file)
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 2453d2d..c53c9d7c 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -384,6 +384,10 @@
     "META": {"sizes": {"includes": [10],}},
     "includes": [2580],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/content/browser/resources/quota/resources.grd": {
+    "META": {"sizes": {"includes": [10],}},
+    "includes": [2590],
+  },
   "<(SHARED_INTERMEDIATE_DIR)/content/browser/webrtc/resources/resources.grd": {
     "META": {"sizes": {"includes": [20],}},
     "includes": [2600],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7dfe271..31f87d5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -84671,7 +84671,7 @@
   <int value="3" label="Renderer crash count"/>
   <int value="4" label="Renderer hang count"/>
   <int value="5" label="Extension renderer crash count"/>
-  <int value="6" label="Child process crash count"/>
+  <int value="6" label="(Obsolete Feb 2022) Child process crash count"/>
   <int value="15" label="Browser launch count"/>
   <int value="16" label="Browser crash count"/>
   <int value="17" label="(Obsolete Dec 2021) Incomplete shutdown count"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 9d673e0d..478f65a6 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -2215,7 +2215,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.SilentUpdatesProfileImportType"
-    enum="AutofillSilentUpdatesProfileImportType" expires_after="M98">
+    enum="AutofillSilentUpdatesProfileImportType" expires_after="M102">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2238,7 +2238,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.UpdateProfileAffectedType.{Decision}"
-    enum="AutofillSettingsVisibleTypes" expires_after="M98">
+    enum="AutofillSettingsVisibleTypes" expires_after="M102">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -2318,7 +2318,7 @@
 
 <histogram
     name="Autofill.ProfileImport.UpdateProfileNumberOfAffectedFields.{Decision}"
-    units="fields" expires_after="M98">
+    units="fields" expires_after="M102">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index d853666..1c10b10 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -1114,6 +1114,18 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.UrlParamFilter.FilteredParamCountExperimental"
+    units="count" expires_after="2023-01-02">
+  <owner>mreichhoff@chromium.org</owner>
+  <owner>bcl@chromium.org</owner>
+  <summary>
+    The number of parameters filtered out of a given request when switching from
+    normal browsing to incognito. Determined by backend classification of
+    parameters. The filtering is currently experimental; this metric will be
+    replaced.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.UserAgentStringType" enum="UserAgentStringType"
     expires_after="2022-06-12">
   <owner>abeyad@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 9d7ca021..098d66f 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -610,6 +610,18 @@
 </histogram>
 
 <histogram
+    name="OptimizationGuide.PageContentAnnotationsService.ModelExecutionLatency.PageEntities"
+    units="ms" expires_after="M106">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records the model execution latency for the PageEntities model only just the
+    annotation request. This does not include any thread hop or queuing delay.
+    Recorded once per model execution attempt.
+  </summary>
+</histogram>
+
+<histogram
     name="OptimizationGuide.PageContentAnnotationsService.PageEntitiesExecutionLatency"
     units="ms" expires_after="M106">
   <owner>mcrouse@chromium.org</owner>
@@ -617,7 +629,8 @@
   <summary>
     Records the execution latency for the PageEntities model from when the model
     manager requests the execution to completion and only if a result is
-    returned. Recorded once per model execution attempt.
+    returned. This includes thread queuing and hopping time. Recorded once per
+    model execution attempt.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index c2de9ef..9eb64a6b 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -292,6 +292,9 @@
 
 <histogram name="Search.ContextualSearch.TranslationsOptInIPHShown"
     enum="BooleanPreviouslyShown" expires_after="2022-04-04">
+  <obsolete>
+    Removed 01/2022
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <owner>contextual-search-eng@google.com</owner>
   <summary>
@@ -303,6 +306,9 @@
 
 <histogram name="Search.ContextualSearch.TranslationsOptInIPHWorked"
     enum="BooleanOptedIn" expires_after="2022-04-04">
+  <obsolete>
+    Removed 01/2022
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <owner>contextual-search-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 266e8a9..b8628b1 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -146,7 +146,7 @@
 </histogram>
 
 <histogram name="Security.HttpsFirstMode.NavigationEvent"
-    enum="HttpsFirstModeNavigationEvent" expires_after="2022-01-31">
+    enum="HttpsFirstModeNavigationEvent" expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -577,7 +577,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.DownloadStarted" enum="SecurityLevel"
-    expires_after="2022-02-01">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -628,7 +628,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.OnComplete" enum="SecurityLevel"
-    expires_after="2022-02-01">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -641,7 +641,7 @@
 </histogram>
 
 <histogram base="true" name="Security.SiteEngagement" units="units"
-    expires_after="2022-02-01">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 8a626df..292ca59 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "dd95274958d71a189df0b6033cc1f0b6e1f4c036",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/d7a3889b04b51adc5e1f8f51d9203847f43fe469/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/57b59d6e2effdc5d3b9dc8da68fa519d9d37f2e0/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "c5cdc3dd4386ea2979b7fd2c7a6e03061d346097",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/e7314895882693974527529802b087ab54f83702/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/57b59d6e2effdc5d3b9dc8da68fa519d9d37f2e0/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "e77b1decbe72adff8b2fba5322e941d478833c9b",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/d7a3889b04b51adc5e1f8f51d9203847f43fe469/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/57b59d6e2effdc5d3b9dc8da68fa519d9d37f2e0/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/page_sets/rendering/rendering_story.py b/tools/perf/page_sets/rendering/rendering_story.py
index 0c35c1f..58a7482 100644
--- a/tools/perf/page_sets/rendering/rendering_story.py
+++ b/tools/perf/page_sets/rendering/rendering_story.py
@@ -64,5 +64,4 @@
 
   def WillStartTracing(self, chrome_trace_config):
     chrome_trace_config.category_filter.AddIncludedCategory('benchmark')
-    chrome_trace_config.category_filter.AddIncludedCategory('gpu')
     chrome_trace_config.category_filter.AddIncludedCategory('v8')
diff --git a/ui/base/interaction/element_tracker.cc b/ui/base/interaction/element_tracker.cc
index 410ebf67d..337c9c2 100644
--- a/ui/base/interaction/element_tracker.cc
+++ b/ui/base/interaction/element_tracker.cc
@@ -38,6 +38,10 @@
   ElementIdentifier identifier() const { return identifier_; }
   ElementContext context() const { return context_; }
 
+  bool HasElement(const TrackedElement* element) const {
+    return base::Contains(element_lookup_, element);
+  }
+
   bool empty() const {
     return elements_.empty() && shown_callbacks_.empty() &&
            activated_callbacks_.empty() && hidden_callbacks_.empty();
@@ -97,7 +101,7 @@
   // Provides a fast lookup into `elements_` by element for checking and
   // removal. Since there could be many elements (e.g. tabs in a browser) we
   // don't want removing a series of them to turn into an O(n^2) operation.
-  std::map<TrackedElement*, std::list<TrackedElement*>::iterator>
+  std::map<const TrackedElement*, std::list<TrackedElement*>::iterator>
       element_lookup_;
 
   base::RepeatingCallbackList<void(TrackedElement*)> shown_callbacks_;
@@ -207,9 +211,11 @@
 ElementTracker::ElementList ElementTracker::GetAllMatchingElementsInAnyContext(
     ElementIdentifier id) {
   ElementList result;
-  for (const auto [element, data] : element_to_data_lookup_) {
-    if (element->identifier() == id)
-      result.push_back(element);
+  for (const auto& [key, data] : element_data_) {
+    if (key.first == id) {
+      std::copy(data.elements().begin(), data.elements().end(),
+                std::back_inserter(result));
+    }
   }
   return result;
 }
@@ -257,26 +263,26 @@
 
 void ElementTracker::NotifyElementShown(TrackedElement* element) {
   GarbageCollector::Frame gc_frame(gc_.get());
-  DCHECK(!base::Contains(element_to_data_lookup_, element));
   ElementData* const element_data =
       GetOrAddElementData(element->identifier(), element->context());
-  element_to_data_lookup_.emplace(element, element_data);
+  DCHECK(!element_data->HasElement(element));
   element_data->NotifyElementShown(element);
 }
 
 void ElementTracker::NotifyElementActivated(TrackedElement* element) {
   GarbageCollector::Frame gc_frame(gc_.get());
-  const auto it = element_to_data_lookup_.find(element);
-  DCHECK(it != element_to_data_lookup_.end());
-  it->second->NotifyElementActivated(element);
+  const auto it =
+      element_data_.find(LookupKey(element->identifier(), element->context()));
+  DCHECK(it != element_data_.end());
+  it->second.NotifyElementActivated(element);
 }
 
 void ElementTracker::NotifyElementHidden(TrackedElement* element) {
   GarbageCollector::Frame gc_frame(gc_.get());
-  const auto it = element_to_data_lookup_.find(element);
-  DCHECK(it != element_to_data_lookup_.end());
-  ElementData* const data = it->second;
-  element_to_data_lookup_.erase(it);
+  const auto it =
+      element_data_.find(LookupKey(element->identifier(), element->context()));
+  DCHECK(it != element_data_.end());
+  ElementData* const data = &it->second;
   data->NotifyElementHidden(element);
   gc_frame.Add(data);
 }
diff --git a/ui/base/interaction/element_tracker.h b/ui/base/interaction/element_tracker.h
index d609af9..72c90bfd 100644
--- a/ui/base/interaction/element_tracker.h
+++ b/ui/base/interaction/element_tracker.h
@@ -168,7 +168,6 @@
   void MaybeCleanup(ElementData* data);
 
   std::map<LookupKey, ElementData> element_data_;
-  std::map<TrackedElement*, ElementData*> element_to_data_lookup_;
   std::unique_ptr<GarbageCollector> gc_;
 };
 
diff --git a/ui/color/chromeos/native_color_mixers_chromeos.cc b/ui/color/chromeos/native_color_mixers_chromeos.cc
index a83f7b1..337703a 100644
--- a/ui/color/chromeos/native_color_mixers_chromeos.cc
+++ b/ui/color/chromeos/native_color_mixers_chromeos.cc
@@ -24,8 +24,6 @@
         high_elevation
             ? color_utils::AlphaBlend(SK_ColorWHITE, gfx::kGoogleGrey900, 0.08f)
             : gfx::kGoogleGrey900;
-    mixer[kColorAshSystemUIMenuBackground] = {
-        SkColorSetA(gfx::kGoogleGrey900, 0xCC)};
     mixer[kColorNativeColor1] = {gfx::kGoogleBlue400};
     mixer[kColorNativeColor1Shade1] = {color_utils::AlphaBlend(
         gfx::kGoogleBlue600, base_color, high_elevation ? 0.4f : 0.3f)};
@@ -42,7 +40,6 @@
             ? gfx::kGoogleGrey700
             : color_utils::AlphaBlend(gfx::kGoogleGrey200, base_color, 0.3f)};
   } else {
-    mixer[kColorAshSystemUIMenuBackground] = {SkColorSetA(SK_ColorWHITE, 0xCC)};
     mixer[kColorNativeColor1] = {gfx::kGoogleBlue500};
     mixer[kColorNativeColor1Shade1] = {gfx::kGoogleBlue300};
     mixer[kColorNativeColor1Shade2] = {gfx::kGoogleBlue100};
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 17d5aa00..2c496c6 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -178,6 +178,9 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #define PLATFORM_SPECIFIC_COLOR_IDS \
   E_CPONLY(kColorAshSystemUIMenuBackground) \
+  E_CPONLY(kColorAshSystemUIMenuIcon) \
+  E_CPONLY(kColorAshSystemUIMenuItemBackgroundSelected) \
+  E_CPONLY(kColorAshSystemUIMenuSeparator) \
   E_CPONLY(kColorNativeColor1) \
   E_CPONLY(kColorNativeColor1Shade1) \
   E_CPONLY(kColorNativeColor1Shade2) \
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 4d2c4ca0..8314d69 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -43,13 +43,22 @@
   cc::PaintFlags flags;
   switch (state) {
     case NativeTheme::kNormal:
-    case NativeTheme::kDisabled:
-      flags.setColor(color_provider->GetColor(kColorMenuBackground));
+    case NativeTheme::kDisabled: {
+      ui::ColorId id = kColorMenuBackground;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+      id = kColorAshSystemUIMenuBackground;
+#endif
+      flags.setColor(color_provider->GetColor(id));
       break;
-    case NativeTheme::kHovered:
-      flags.setColor(
-          color_provider->GetColor(kColorMenuItemBackgroundSelected));
+    }
+    case NativeTheme::kHovered: {
+      ui::ColorId id = kColorMenuItemBackgroundSelected;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+      id = kColorAshSystemUIMenuItemBackgroundSelected;
+#endif
+      flags.setColor(color_provider->GetColor(id));
       break;
+    }
     default:
       NOTREACHED() << "Invalid state " << state;
       break;
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 21b630c..497b0af4 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -113,7 +113,11 @@
 
 void VerticalSeparator::OnThemeChanged() {
   Separator::OnThemeChanged();
-  SetColor(GetColorProvider()->GetColor(ui::kColorMenuSeparator));
+  ui::ColorId id = ui::kColorMenuSeparator;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  id = ui::kColorAshSystemUIMenuSeparator;
+#endif
+  SetColor(GetColorProvider()->GetColor(id));
 }
 
 BEGIN_METADATA(VerticalSeparator, Separator)
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index 25317d33..7706c760 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -393,18 +393,17 @@
   const int border_radius = menu_config.CornerRadiusForMenu(
       content_view_->GetMenuItem()->GetMenuController());
   ui::ColorId id = ui::kColorMenuBackground;
-  const bool use_ash_system_ui_layout = content_view_->GetMenuItem()
-                                            ->GetMenuController()
-                                            ->use_ash_system_ui_layout();
-#if BUILDFLAG(IS_CHROMEOS)
-  if (use_ash_system_ui_layout)
-    id = ui::kColorAshSystemUIMenuBackground;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  id = ui::kColorAshSystemUIMenuBackground;
 #endif
   const SkColor color =
       GetWidget() ? GetColorProvider()->GetColor(id) : gfx::kPlaceholderColor;
   auto bubble_border = std::make_unique<BubbleBorder>(
       arrow_, BubbleBorder::STANDARD_SHADOW, color);
-  if (use_ash_system_ui_layout || border_radius > 0) {
+  if (content_view_->GetMenuItem()
+          ->GetMenuController()
+          ->use_ash_system_ui_layout() ||
+      border_radius > 0) {
     bubble_border->SetCornerRadius(border_radius);
     bubble_border->set_md_shadow_elevation(
         menu_config.touchable_menu_shadow_elevation);
diff --git a/url/origin.h b/url/origin.h
index b26cbb0f..2c1e19c 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -42,6 +42,7 @@
 
 namespace blink {
 class SecurityOrigin;
+class SecurityOriginTest;
 }  // namespace blink
 
 namespace ipc_fuzzer {
@@ -308,6 +309,7 @@
 
  private:
   friend class blink::SecurityOrigin;
+  friend class blink::SecurityOriginTest;
   // SchemefulSite needs access to the serialization/deserialization logic which
   // includes the nonce.
   friend class net::SchemefulSite;