diff --git a/libraries.module b/libraries.module
index 404d189..b557541 100644
--- a/libraries.module
+++ b/libraries.module
@@ -5,10 +5,9 @@
  * External library handling for Drupal modules.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\libraries\Hook\LibrariesHooks;
 use Drupal\Core\DrupalKernel;
-use Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface;
-use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
-use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorInterface;
 use Symfony\Component\Yaml\Parser;
 use Drupal\Core\Extension\Dependency;
 
@@ -17,38 +16,9 @@ use Drupal\Core\Extension\Dependency;
  *
  * Register external asset libraries with Drupal core's library APIs.
  */
+#[LegacyHook]
 function libraries_library_info_build() {
-  /** @var \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager */
-  $library_manager = \Drupal::service('libraries.manager');
-  $attachable_libraries = [];
-  $libraries_with_errors = [];
-  foreach ($library_manager->getRequiredLibraryIds() as $external_library_id) {
-    try {
-      $external_library = $library_manager->getLibrary($external_library_id);
-      $library_type = $external_library->getType();
-      if ($library_type instanceof AttachableAssetLibraryRegistrationInterface) {
-        $attachable_libraries += $library_type->getAttachableAssetLibraries($external_library, $library_manager);
-      }
-    }
-    catch (\Exception $e) {
-      // Library-specific exceptions should not be allowed to kill the rest of
-      // the build process, but should be logged.
-      if ($e instanceof LibraryIdAccessorInterface || $e instanceof LibraryAccessorInterface) {
-        $libraries_with_errors[] = $external_library_id;
-        \Drupal::logger('libraries')->error($e->getMessage());
-      }
-      else {
-        // Re-throw exceptions that are not library-specific.
-        throw $e;
-      }
-    }
-  }
-  // If we had library specific errors also log an informative message to
-  // tell admins that detection will not be run again without a cache clear.
-  if ($libraries_with_errors) {
-    \Drupal::logger('libraries')->error('The following external libraries could not successfully be registered with Drupal core: @libs. See earlier log entries for more details. Once these issues are addressed please be sure to clear your Drupal library cache to ensure external library detection is run again.', ['@libs' => implode(',', $libraries_with_errors)]);
-  }
-  return $attachable_libraries;
+  return \Drupal::service(LibrariesHooks::class)->libraryInfoBuild();
 }
 
 /**
@@ -111,14 +81,13 @@ function libraries_get_libraries() {
   $config = DrupalKernel::findSitePath(\Drupal::request());
 
   // @todo core/libraries
-
   // Similar to 'modules' and 'themes' directories inside an installation
   // profile, installation profiles may want to place libraries into a
   // 'libraries' directory.
   if ($profile = \Drupal::installProfile()) {
     $profile_path = \Drupal::service('extension.list.profile')->getPath($profile);
     $searchdir[] = "$profile_path/libraries";
-  };
+  }
 
   // Search sites/all/libraries for backwards-compatibility.
   $searchdir[] = 'sites/all/libraries';
diff --git a/libraries.services.yml b/libraries.services.yml
index ad95851..4c258f6 100644
--- a/libraries.services.yml
+++ b/libraries.services.yml
@@ -70,3 +70,7 @@ services:
       - { name: cache.bin }
     factory: cache_factory:get
     arguments: [library]
+
+  Drupal\libraries\Hook\LibrariesHooks:
+    class: Drupal\libraries\Hook\LibrariesHooks
+    autowire: true
diff --git a/src/Config/LibrariesConfigSubscriber.php b/src/Config/LibrariesConfigSubscriber.php
index 6578f42..45030ad 100644
--- a/src/Config/LibrariesConfigSubscriber.php
+++ b/src/Config/LibrariesConfigSubscriber.php
@@ -44,7 +44,7 @@ class LibrariesConfigSubscriber implements EventSubscriberInterface {
   /**
    * {@inheritdoc}
    */
-  public static function getSubscribedEvents() {
+  public static function getSubscribedEvents(): array {
     return [ConfigEvents::SAVE => 'onConfigSave'];
   }
 
diff --git a/src/Hook/LibrariesHooks.php b/src/Hook/LibrariesHooks.php
new file mode 100644
index 0000000..8a0d482
--- /dev/null
+++ b/src/Hook/LibrariesHooks.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\libraries\Hook;
+
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorInterface;
+use Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for libraries.
+ */
+class LibrariesHooks {
+
+  /**
+   * Implements hook_library_info_build().
+   *
+   * Register external asset libraries with Drupal core's library APIs.
+   */
+  #[Hook('library_info_build')]
+  public function libraryInfoBuild() {
+    /** @var \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager */
+    $library_manager = \Drupal::service('libraries.manager');
+    $attachable_libraries = [];
+    $libraries_with_errors = [];
+    foreach ($library_manager->getRequiredLibraryIds() as $external_library_id) {
+      try {
+        $external_library = $library_manager->getLibrary($external_library_id);
+        $library_type = $external_library->getType();
+        if ($library_type instanceof AttachableAssetLibraryRegistrationInterface) {
+          $attachable_libraries += $library_type->getAttachableAssetLibraries($external_library, $library_manager);
+        }
+      }
+      catch (\Exception $e) {
+        // Library-specific exceptions should not be allowed to kill the rest of
+        // the build process, but should be logged.
+        if ($e instanceof LibraryIdAccessorInterface || $e instanceof LibraryAccessorInterface) {
+          $libraries_with_errors[] = $external_library_id;
+          \Drupal::logger('libraries')->error($e->getMessage());
+        }
+        else {
+          // Re-throw exceptions that are not library-specific.
+          throw $e;
+        }
+      }
+    }
+    // If we had library specific errors also log an informative message to
+    // tell admins that detection will not be run again without a cache clear.
+    if ($libraries_with_errors) {
+      \Drupal::logger('libraries')->error('The following external libraries could not successfully be registered with Drupal core: @libs. See earlier log entries for more details. Once these issues are addressed please be sure to clear your Drupal library cache to ensure external library detection is run again.', [
+        '@libs' => implode(',', $libraries_with_errors),
+      ]);
+    }
+    return $attachable_libraries;
+  }
+
+}
diff --git a/tests/modules/libraries_test/libraries_test.module b/tests/modules/libraries_test/libraries_test.module
index 89b0eca..7eb92c1 100644
--- a/tests/modules/libraries_test/libraries_test.module
+++ b/tests/modules/libraries_test/libraries_test.module
@@ -5,294 +5,24 @@
  * Tests the library detection and loading.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\libraries_test\Hook\LibrariesTestHooks;
 use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Messenger\MessengerTrait;
 
 /**
  * Implements hook_libraries_info().
  */
+#[LegacyHook]
 function libraries_test_libraries_info() {
-  // Test library detection.
-  $libraries['example_missing'] = [
-    'name' => 'Example missing',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/missing',
-  ];
-  $libraries['example_undetected_version'] = [
-    'name' => 'Example undetected version',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
-    'version callback' => '_libraries_test_return_version',
-    'version arguments' => [FALSE],
-  ];
-  $libraries['example_unsupported_version'] = [
-    'name' => 'Example unsupported version',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
-    'version callback' => '_libraries_test_return_version',
-    'version arguments' => ['1'],
-    'versions' => [
-      '2' => [],
-    ],
-  ];
-
-  $libraries['example_supported_version'] = [
-    'name' => 'Example supported version',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
-    'version callback' => '_libraries_test_return_version',
-    'version arguments' => ['1'],
-    'versions' => [
-      '1' => [],
-    ],
-  ];
-
-  // Test the default version callback.
-  $libraries['example_default_version_callback'] = [
-    'name' => 'Example default version callback',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version arguments' => [
-      'file' => 'README.txt',
-      // Version 1
-      'pattern' => '/Version (\d+)/',
-      'lines' => 5,
-    ],
-  ];
-
-  // Test a multiple-parameter version callback.
-  $libraries['example_multiple_parameter_version_callback'] = [
-    'name' => 'Example multiple parameter version callback',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    // Version 1
-    'version callback' => '_libraries_test_get_version',
-    'version arguments' => ['README.txt', '/Version (\d+)/', 5],
-  ];
-
-  // Test a top-level files property.
-  $libraries['example_files'] = [
-    'name' => 'Example files',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'files' => [
-      'js' => ['example_1.js'],
-      'css' => ['example_1.css'],
-      'php' => ['example_1.php'],
-    ],
-  ];
-
-  // Test loading of integration files.
-  // Normally added by the corresponding module via hook_libraries_info_alter(),
-  // these files should be automatically loaded when the library is loaded.
-  $libraries['example_integration_files'] = [
-    'name' => 'Example integration files',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'integration files' => [
-      'libraries_test' => [
-        'js' => ['libraries_test.js'],
-        'css' => ['libraries_test.css'],
-        'php' => ['libraries_test.inc'],
-      ],
-    ],
-  ];
-
-  // Test version overloading.
-  $libraries['example_versions'] = [
-    'name' => 'Example versions',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '2',
-    'versions' => [
-      '1' => [
-        'files' => [
-          'js' => ['example_1.js'],
-          'css' => ['example_1.css'],
-          'php' => ['example_1.php'],
-        ],
-      ],
-      '2' => [
-        'files' => [
-          'js' => ['example_2.js'],
-          'css' => ['example_2.css'],
-          'php' => ['example_2.php'],
-        ],
-      ],
-    ],
-  ];
-
-  // Test variant detection.
-  $libraries['example_variant_missing'] = [
-    'name' => 'Example variant missing',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'variants' => [
-      'example_variant' => [
-        'files' => [
-          'js' => ['example_3.js'],
-          'css' => ['example_3.css'],
-          'php' => ['example_3.php'],
-        ],
-        'variant callback' => '_libraries_test_return_installed',
-        'variant arguments' => [FALSE],
-      ],
-    ],
-  ];
-
-  $libraries['example_variant'] = [
-    'name' => 'Example variant',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'variants' => [
-      'example_variant' => [
-        'files' => [
-          'js' => ['example_3.js'],
-          'css' => ['example_3.css'],
-          'php' => ['example_3.php'],
-        ],
-        'variant callback' => '_libraries_test_return_installed',
-        'variant arguments' => [TRUE],
-      ],
-    ],
-  ];
-
-  // Test correct behaviour with multiple versions and multiple variants.
-  $libraries['example_versions_and_variants'] = [
-    'name' => 'Example versions and variants',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '2',
-    'versions' => [
-      '1' => [
-        'variants' => [
-          'example_variant_1' => [
-            'files' => [
-              'js' => ['example_1.js'],
-              'css' => ['example_1.css'],
-              'php' => ['example_1.php'],
-            ],
-            'variant callback' => '_libraries_test_return_installed',
-            'variant arguments' => [TRUE],
-          ],
-          'example_variant_2' => [
-            'files' => [
-              'js' => ['example_2.js'],
-              'css' => ['example_2.css'],
-              'php' => ['example_2.php'],
-            ],
-            'variant callback' => '_libraries_test_return_installed',
-            'variant arguments' => [TRUE],
-          ],
-        ],
-      ],
-      '2' => [
-        'variants' => [
-          'example_variant_1' => [
-            'files' => [
-              'js' => ['example_3.js'],
-              'css' => ['example_3.css'],
-              'php' => ['example_3.php'],
-            ],
-            'variant callback' => '_libraries_test_return_installed',
-            'variant arguments' => [TRUE],
-          ],
-          'example_variant_2' => [
-            'files' => [
-              'js' => ['example_4.js'],
-              'css' => ['example_4.css'],
-              'php' => ['example_4.php'],
-            ],
-            'variant callback' => '_libraries_test_return_installed',
-            'variant arguments' => [TRUE],
-          ],
-        ],
-      ],
-    ],
-  ];
-
-  // Test dependency loading.
-  // We add one file to each library to be able to verify if it was loaded with
-  // libraries_load().
-  // This library acts as a dependency for the libraries below.
-  $libraries['example_dependency'] = [
-    'name' => 'Example dependency',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1.1',
-    'files' => ['js' => ['example_1.js']],
-  ];
-  $libraries['example_dependency_missing'] = [
-    'name' => 'Example dependency missing',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'dependencies' => ['example_missing'],
-    'files' => ['js' => ['example_1.js']],
-  ];
-  $libraries['example_dependency_incompatible'] = [
-    'name' => 'Example dependency incompatible',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'dependencies' => ['example_dependency (>1.1)'],
-    'files' => ['js' => ['example_1.js']],
-  ];
-  $libraries['example_dependency_compatible'] = [
-    'name' => 'Example dependency compatible',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'dependencies' => ['example_dependency (>=1.1)'],
-    'files' => ['js' => ['example_1.js']],
-  ];
-
-  // Test the applying of callbacks.
-  $libraries['example_callback'] = [
-    'name' => 'Example callback',
-    'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
-    'version' => '1',
-    'versions' => [
-      '1' => [
-        'variants' => [
-          'example_variant' => [
-            // These keys are for testing purposes only.
-            'info callback' => 'not applied',
-            'pre-detect callback' => 'not applied',
-            'post-detect callback' => 'not applied',
-            'pre-load callback' => 'not applied',
-            'post-load callback' => 'not applied',
-          ],
-        ],
-        // These keys are for testing purposes only.
-        'info callback' => 'not applied',
-        'pre-detect callback' => 'not applied',
-        'post-detect callback' => 'not applied',
-        'pre-load callback' => 'not applied',
-        'post-load callback' => 'not applied',
-      ],
-    ],
-    'variants' => [
-      'example_variant' => [
-        // These keys are for testing purposes only.
-        'info callback' => 'not applied',
-        'pre-detect callback' => 'not applied',
-        'post-detect callback' => 'not applied',
-        'pre-load callback' => 'not applied',
-        'post-load callback' => 'not applied',
-      ],
-    ],
-    'callbacks' => [
-      'info' => ['_libraries_test_info_callback'],
-      'pre-detect' => ['_libraries_test_pre_detect_callback'],
-      'post-detect' => ['_libraries_test_post_detect_callback'],
-      'pre-load' => ['_libraries_test_pre_load_callback'],
-      'post-load' => ['_libraries_test_post_load_callback'],
-    ],
-    // These keys are for testing purposes only.
-    'info callback' => 'not applied',
-    'pre-detect callback' => 'not applied',
-    'post-detect callback' => 'not applied',
-    'pre-load callback' => 'not applied',
-    'post-load callback' => 'not applied',
-  ];
-
-  return $libraries;
+  return \Drupal::service(LibrariesTestHooks::class)->librariesInfo();
 }
 
 /**
  * Implements hook_libraries_info_file_paths()
  */
+#[LegacyHook]
 function libraries_test_libraries_info_file_paths() {
-  return [\Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example'];
+  return \Drupal::service(LibrariesTestHooks::class)->librariesInfoFilePaths();
 }
 
 /**
@@ -469,30 +199,7 @@ function _libraries_test_callback(&$library, $version, $variant, $group) {
 /**
  * Implements hook_menu().
  */
+#[LegacyHook]
 function libraries_test_menu() {
-  $items['libraries_test/files'] = [
-    'title' => 'Test files',
-    'route_name' => 'libraries_test_files',
-  ];
-  $items['libraries_test/integration_files'] = [
-    'title' => 'Test integration files',
-    'route_name' => 'libraries_test_integration_files',
-  ];
-  $items['libraries_test/versions'] = [
-    'title' => 'Test version loading',
-    'route_name' => 'libraries_test_versions',
-  ];
-  $items['libraries_test/variant'] = [
-    'title' => 'Test variant loading',
-    'route_name' => 'libraries_test_variant',
-  ];
-  $items['libraries_test/versions_and_variants'] = [
-    'title' => 'Test concurrent version and variant loading',
-    'route_name' => 'libraries_test_versions_and_variants',
-  ];
-  $items['libraries_test/cache'] = [
-    'title' => 'Test caching of library information',
-    'route_name' => 'libraries_test_cache',
-  ];
-  return $items;
+  return \Drupal::service(LibrariesTestHooks::class)->menu();
 }
diff --git a/tests/modules/libraries_test/libraries_test.services.yml b/tests/modules/libraries_test/libraries_test.services.yml
new file mode 100644
index 0000000..84bfcc0
--- /dev/null
+++ b/tests/modules/libraries_test/libraries_test.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\libraries_test\Hook\LibrariesTestHooks:
+    class: Drupal\libraries_test\Hook\LibrariesTestHooks
+    autowire: true
diff --git a/tests/modules/libraries_test/src/Hook/LibrariesTestHooks.php b/tests/modules/libraries_test/src/Hook/LibrariesTestHooks.php
new file mode 100644
index 0000000..7127a2c
--- /dev/null
+++ b/tests/modules/libraries_test/src/Hook/LibrariesTestHooks.php
@@ -0,0 +1,437 @@
+<?php
+
+namespace Drupal\libraries_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for libraries_test.
+ */
+class LibrariesTestHooks {
+
+  /**
+   * Implements hook_libraries_info().
+   */
+  #[Hook('libraries_info')]
+  public function librariesInfo() {
+    // Test library detection.
+    $libraries['example_missing'] = [
+      'name' => 'Example missing',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/missing',
+    ];
+    $libraries['example_undetected_version'] = [
+      'name' => 'Example undetected version',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
+      'version callback' => '_libraries_test_return_version',
+      'version arguments' => [
+        FALSE,
+      ],
+    ];
+    $libraries['example_unsupported_version'] = [
+      'name' => 'Example unsupported version',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
+      'version callback' => '_libraries_test_return_version',
+      'version arguments' => [
+        '1',
+      ],
+      'versions' => [
+        '2' => [],
+      ],
+    ];
+    $libraries['example_supported_version'] = [
+      'name' => 'Example supported version',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests',
+      'version callback' => '_libraries_test_return_version',
+      'version arguments' => [
+        '1',
+      ],
+      'versions' => [
+        '1' => [],
+      ],
+    ];
+    // Test the default version callback.
+    $libraries['example_default_version_callback'] = [
+      'name' => 'Example default version callback',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version arguments' => [
+        'file' => 'README.txt',
+              // Version 1.
+        'pattern' => '/Version (\d+)/',
+        'lines' => 5,
+      ],
+    ];
+    // Test a multiple-parameter version callback.
+    $libraries['example_multiple_parameter_version_callback'] = [
+      'name' => 'Example multiple parameter version callback',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+          // Version 1.
+      'version callback' => '_libraries_test_get_version',
+      'version arguments' => [
+        'README.txt',
+        '/Version (\d+)/',
+        5,
+      ],
+    ];
+    // Test a top-level files property.
+    $libraries['example_files'] = [
+      'name' => 'Example files',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'files' => [
+        'js' => [
+          'example_1.js',
+        ],
+        'css' => [
+          'example_1.css',
+        ],
+        'php' => [
+          'example_1.php',
+        ],
+      ],
+    ];
+    // Test loading of integration files.
+    // Normally added by the corresponding module via hook_libraries_info_alter(),
+    // these files should be automatically loaded when the library is loaded.
+    $libraries['example_integration_files'] = [
+      'name' => 'Example integration files',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'integration files' => [
+        'libraries_test' => [
+          'js' => [
+            'libraries_test.js',
+          ],
+          'css' => [
+            'libraries_test.css',
+          ],
+          'php' => [
+            'libraries_test.inc',
+          ],
+        ],
+      ],
+    ];
+    // Test version overloading.
+    $libraries['example_versions'] = [
+      'name' => 'Example versions',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '2',
+      'versions' => [
+        '1' => [
+          'files' => [
+            'js' => [
+              'example_1.js',
+            ],
+            'css' => [
+              'example_1.css',
+            ],
+            'php' => [
+              'example_1.php',
+            ],
+          ],
+        ],
+        '2' => [
+          'files' => [
+            'js' => [
+              'example_2.js',
+            ],
+            'css' => [
+              'example_2.css',
+            ],
+            'php' => [
+              'example_2.php',
+            ],
+          ],
+        ],
+      ],
+    ];
+    // Test variant detection.
+    $libraries['example_variant_missing'] = [
+      'name' => 'Example variant missing',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'variants' => [
+        'example_variant' => [
+          'files' => [
+            'js' => [
+              'example_3.js',
+            ],
+            'css' => [
+              'example_3.css',
+            ],
+            'php' => [
+              'example_3.php',
+            ],
+          ],
+          'variant callback' => '_libraries_test_return_installed',
+          'variant arguments' => [
+            FALSE,
+          ],
+        ],
+      ],
+    ];
+    $libraries['example_variant'] = [
+      'name' => 'Example variant',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'variants' => [
+        'example_variant' => [
+          'files' => [
+            'js' => [
+              'example_3.js',
+            ],
+            'css' => [
+              'example_3.css',
+            ],
+            'php' => [
+              'example_3.php',
+            ],
+          ],
+          'variant callback' => '_libraries_test_return_installed',
+          'variant arguments' => [
+            TRUE,
+          ],
+        ],
+      ],
+    ];
+    // Test correct behaviour with multiple versions and multiple variants.
+    $libraries['example_versions_and_variants'] = [
+      'name' => 'Example versions and variants',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '2',
+      'versions' => [
+        '1' => [
+          'variants' => [
+            'example_variant_1' => [
+              'files' => [
+                'js' => [
+                  'example_1.js',
+                ],
+                'css' => [
+                  'example_1.css',
+                ],
+                'php' => [
+                  'example_1.php',
+                ],
+              ],
+              'variant callback' => '_libraries_test_return_installed',
+              'variant arguments' => [
+                TRUE,
+              ],
+            ],
+            'example_variant_2' => [
+              'files' => [
+                'js' => [
+                  'example_2.js',
+                ],
+                'css' => [
+                  'example_2.css',
+                ],
+                'php' => [
+                  'example_2.php',
+                ],
+              ],
+              'variant callback' => '_libraries_test_return_installed',
+              'variant arguments' => [
+                TRUE,
+              ],
+            ],
+          ],
+        ],
+        '2' => [
+          'variants' => [
+            'example_variant_1' => [
+              'files' => [
+                'js' => [
+                  'example_3.js',
+                ],
+                'css' => [
+                  'example_3.css',
+                ],
+                'php' => [
+                  'example_3.php',
+                ],
+              ],
+              'variant callback' => '_libraries_test_return_installed',
+              'variant arguments' => [
+                TRUE,
+              ],
+            ],
+            'example_variant_2' => [
+              'files' => [
+                'js' => [
+                  'example_4.js',
+                ],
+                'css' => [
+                  'example_4.css',
+                ],
+                'php' => [
+                  'example_4.php',
+                ],
+              ],
+              'variant callback' => '_libraries_test_return_installed',
+              'variant arguments' => [
+                TRUE,
+              ],
+            ],
+          ],
+        ],
+      ],
+    ];
+    // Test dependency loading.
+    // We add one file to each library to be able to verify if it was loaded with
+    // libraries_load().
+    // This library acts as a dependency for the libraries below.
+    $libraries['example_dependency'] = [
+      'name' => 'Example dependency',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1.1',
+      'files' => [
+        'js' => [
+          'example_1.js',
+        ],
+      ],
+    ];
+    $libraries['example_dependency_missing'] = [
+      'name' => 'Example dependency missing',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'dependencies' => [
+        'example_missing',
+      ],
+      'files' => [
+        'js' => [
+          'example_1.js',
+        ],
+      ],
+    ];
+    $libraries['example_dependency_incompatible'] = [
+      'name' => 'Example dependency incompatible',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'dependencies' => [
+        'example_dependency (>1.1)',
+      ],
+      'files' => [
+        'js' => [
+          'example_1.js',
+        ],
+      ],
+    ];
+    $libraries['example_dependency_compatible'] = [
+      'name' => 'Example dependency compatible',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'dependencies' => [
+        'example_dependency (>=1.1)',
+      ],
+      'files' => [
+        'js' => [
+          'example_1.js',
+        ],
+      ],
+    ];
+    // Test the applying of callbacks.
+    $libraries['example_callback'] = [
+      'name' => 'Example callback',
+      'library path' => \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+      'version' => '1',
+      'versions' => [
+        '1' => [
+          'variants' => [
+            'example_variant' => [
+                          // These keys are for testing purposes only.
+              'info callback' => 'not applied',
+              'pre-detect callback' => 'not applied',
+              'post-detect callback' => 'not applied',
+              'pre-load callback' => 'not applied',
+              'post-load callback' => 'not applied',
+            ],
+          ],
+                  // These keys are for testing purposes only.
+          'info callback' => 'not applied',
+          'pre-detect callback' => 'not applied',
+          'post-detect callback' => 'not applied',
+          'pre-load callback' => 'not applied',
+          'post-load callback' => 'not applied',
+        ],
+      ],
+      'variants' => [
+        'example_variant' => [
+                  // These keys are for testing purposes only.
+          'info callback' => 'not applied',
+          'pre-detect callback' => 'not applied',
+          'post-detect callback' => 'not applied',
+          'pre-load callback' => 'not applied',
+          'post-load callback' => 'not applied',
+        ],
+      ],
+      'callbacks' => [
+        'info' => [
+          '_libraries_test_info_callback',
+        ],
+        'pre-detect' => [
+          '_libraries_test_pre_detect_callback',
+        ],
+        'post-detect' => [
+          '_libraries_test_post_detect_callback',
+        ],
+        'pre-load' => [
+          '_libraries_test_pre_load_callback',
+        ],
+        'post-load' => [
+          '_libraries_test_post_load_callback',
+        ],
+      ],
+          // These keys are for testing purposes only.
+      'info callback' => 'not applied',
+      'pre-detect callback' => 'not applied',
+      'post-detect callback' => 'not applied',
+      'pre-load callback' => 'not applied',
+      'post-load callback' => 'not applied',
+    ];
+    return $libraries;
+  }
+
+  /**
+   * Implements hook_libraries_info_file_paths()
+   */
+  #[Hook('libraries_info_file_paths')]
+  public function librariesInfoFilePaths() {
+    return [
+      \Drupal::service('extension.list.module')->getPath('libraries') . '/tests/example',
+    ];
+  }
+
+  /**
+   * Implements hook_menu().
+   */
+  #[Hook('menu')]
+  public function menu() {
+    $items['libraries_test/files'] = [
+      'title' => 'Test files',
+      'route_name' => 'libraries_test_files',
+    ];
+    $items['libraries_test/integration_files'] = [
+      'title' => 'Test integration files',
+      'route_name' => 'libraries_test_integration_files',
+    ];
+    $items['libraries_test/versions'] = [
+      'title' => 'Test version loading',
+      'route_name' => 'libraries_test_versions',
+    ];
+    $items['libraries_test/variant'] = [
+      'title' => 'Test variant loading',
+      'route_name' => 'libraries_test_variant',
+    ];
+    $items['libraries_test/versions_and_variants'] = [
+      'title' => 'Test concurrent version and variant loading',
+      'route_name' => 'libraries_test_versions_and_variants',
+    ];
+    $items['libraries_test/cache'] = [
+      'title' => 'Test caching of library information',
+      'route_name' => 'libraries_test_cache',
+    ];
+    return $items;
+  }
+
+}
diff --git a/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php b/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php
index 71ba6f1..db57f76 100644
--- a/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php
+++ b/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Functional\ExternalLibrary\Definition;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -13,6 +15,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @todo Make this a kernel test when https://www.drupal.org/node/2571475 is in.
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class DefinitionDiscoveryFactoryTest extends BrowserTestBase {
 
   /**
@@ -68,7 +72,7 @@ class DefinitionDiscoveryFactoryTest extends BrowserTestBase {
       'version_detector' => [
         'id' => 'static',
         'configuration' => [
-          'version' => '1.0.0'
+          'version' => '1.0.0',
         ],
       ],
       'remote_url' => 'http://example.com',
diff --git a/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php b/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php
index 146f19a..2229fa7 100644
--- a/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php
+++ b/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\Asset;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
 
 /**
@@ -9,6 +11,8 @@ use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
  *
  * @group libraries
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class AssetLibraryTest extends AssetLibraryTestBase {
 
   /**
@@ -57,19 +61,21 @@ class AssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'external',
         'data' => 'http://example.com/example.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'external',
         'data' => 'http://example.com/example.js',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
   }
@@ -93,20 +99,22 @@ class AssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_library/example.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_library/example.js',
         'version' => '1.0.0',
         'minified' => FALSE,
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
   }
diff --git a/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php b/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php
index 6726071..950040b 100644
--- a/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php
+++ b/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\Asset;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
 
 /**
@@ -9,6 +11,8 @@ use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
  *
  * @group libraries
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class MultipleAssetLibraryTest extends AssetLibraryTestBase {
 
   /**
@@ -63,19 +67,21 @@ class MultipleAssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'external',
         'data' => 'http://example.com/example.first.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'external',
         'data' => 'http://example.com/example.first.js',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
 
@@ -88,19 +94,21 @@ class MultipleAssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'external',
         'data' => 'http://example.com/example.second.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'external',
         'data' => 'http://example.com/example.second.js',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
   }
@@ -125,20 +133,22 @@ class MultipleAssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.first.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.first.js',
         'version' => '1.0.0',
         'minified' => FALSE,
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
 
@@ -151,20 +161,22 @@ class MultipleAssetLibraryTest extends AssetLibraryTestBase {
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.second.css',
         'version' => '1.0.0',
-      ]],
+      ],
+      ],
       'js' => [[
         'group' => -100,
         'type' => 'file',
         'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.second.js',
         'version' => '1.0.0',
         'minified' => FALSE,
-      ]],
+      ],
+      ],
       'dependencies' => [],
       'license' => [
         'name' => $this->getLicenseName(),
         'url' => 'https://www.drupal.org/licensing/faq',
         'gpl-compatible' => TRUE,
-      ]
+      ],
     ];
     $this->assertEquals($expected, $library);
   }
diff --git a/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php b/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php
index 39a361d..3a38f9c 100644
--- a/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php
+++ b/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php
@@ -2,7 +2,8 @@
 
 namespace Drupal\Tests\libraries\Kernel\ExternalLibrary;
 
-use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
 
 /**
@@ -10,6 +11,8 @@ use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
  *
  * @group libraries
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class GlobalLocatorTest extends LibraryTypeKernelTestBase {
 
   /**
diff --git a/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php b/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php
index 75098e7..a2d29bf 100644
--- a/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php
+++ b/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\PhpFile;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
 use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
 
@@ -10,6 +12,8 @@ use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
  *
  * @group libraries
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class PhpFileLibraryTest extends LibraryTypeKernelTestBase {
 
   /**
diff --git a/tests/src/Kernel/LibrariesApi/LibrariesUnitTest.php b/tests/src/Kernel/LibrariesApi/LibrariesUnitTest.php
index 247c116..4fd3332 100644
--- a/tests/src/Kernel/LibrariesApi/LibrariesUnitTest.php
+++ b/tests/src/Kernel/LibrariesApi/LibrariesUnitTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Kernel\LibrariesApi;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\KernelTests\KernelTestBase;
 
 /**
@@ -9,6 +11,8 @@ use Drupal\KernelTests\KernelTestBase;
  *
  * @group libraries
  */
+#[Group('libraries')]
+#[RunTestsInSeparateProcesses]
 class LibrariesUnitTest extends KernelTestBase {
 
   /**
@@ -19,7 +23,7 @@ class LibrariesUnitTest extends KernelTestBase {
   /**
    * Tests libraries_get_path().
    */
-  function testLibrariesGetPath() {
+  public function testLibrariesGetPath() {
     // Note that, even though libraries_get_path() doesn't find the 'example'
     // library, we are able to make it 'installed' by specifying the 'library
     // path' up-front. This is only used for testing purposed and is strongly
@@ -31,7 +35,7 @@ class LibrariesUnitTest extends KernelTestBase {
   /**
    * Tests libraries_prepare_files().
    */
-  function testLibrariesPrepareFiles() {
+  public function testLibrariesPrepareFiles() {
     $expected = [
       'files' => [
         'js' => ['example.js' => []],
diff --git a/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php b/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php
index 8e8b41d..abcbe90 100644
--- a/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php
+++ b/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\libraries\Unit\Plugin\libraries\VersionDetector;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\DataProvider;
 use Prophecy\PhpUnit\ProphecyTrait;
 use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
 use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
@@ -17,6 +19,7 @@ use org\bovigo\vfs\vfsStream;
  *
  * @coversDefaultClass \Drupal\libraries\Plugin\libraries\VersionDetector\LinePatternDetector
  */
+#[Group('libraries')]
 class LinePatternDetectorTest extends UnitTestCase {
 
   use ProphecyTrait;
@@ -54,6 +57,7 @@ class LinePatternDetectorTest extends UnitTestCase {
    *
    * @covers ::detectVersion
    */
+  #[DataProvider('providerTestDetectVersionNoVersion')]
   public function testDetectVersionNoVersion($configuration, $file_contents) {
     $library = $this->setupLibrary();
 
@@ -114,6 +118,7 @@ EOF
    *
    * @covers ::detectVersion
    */
+  #[DataProvider('providerTestDetectVersion')]
   public function testDetectVersion($configuration, $file_contents, $version) {
     $library = $this->setupLibrary();
 
