Skip to content
Snippets Groups Projects
graphics.c 137 KiB
Newer Older
      g_assert_cmpint (srt_egl_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_null (iter);
    }
}

static void
test_egl_external_platform (Fixture *f,
                            gconstpointer context)
{
  const Config *config = context;
  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
  g_autoptr(SrtObjectList) exts = NULL;
  g_autofree gchar *resolved = NULL;
  const GList *iter;
  const char * const multiarchs[] = { "x86_64-mock-abi", NULL };

  g_test_message ("Entering %s", G_STRFUNC);

  if (config != NULL)
    g_test_message ("Mode: %s", icd_mode_to_string (config->icd_mode));

  if (f->sysroot != NULL)
    g_test_message ("Sysroot: %s", glnx_basename (f->sysroot));

  srt_system_info_set_environ (info, f->fake_icds_envp);
  srt_system_info_set_sysroot (info, f->sysroot);
  srt_system_info_set_helpers_path (info, f->builddir);

  exts = srt_system_info_list_egl_external_platforms (info, multiarchs);

  for (iter = exts; iter != NULL; iter = iter->next)
    {
      GError *error = NULL;

      g_test_message ("External platform: %s", srt_egl_external_platform_get_json_path (iter->data));

      if (srt_egl_external_platform_check_error (iter->data, &error))
        {
          g_test_message ("\tlibrary: %s",
                          srt_egl_external_platform_get_library_path (iter->data));
        }
      else
        {
          g_test_message ("\terror: %s", error->message);
          g_clear_error (&error);
        }
    }

  if (config != NULL && config->icd_mode == ICD_MODE_FLATPAK)
    {
      iter = exts;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_EXPLICIT_DIRS)
    {
      iter = exts;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/AAA.json");
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      /* We sort lexicographically with strcmp(), so BBB comes before a. */
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/BBB.json");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/a.json");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/b.json");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/soname_zlib_dup.json");
      /* In the ECL ICDs test case, this shows up as a duplicate, but since
       * we're not looking in /egl2 here, it does not */
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);
      assert_egl_external_platform_no_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/egl1/z.json");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_UNSUPPORTED);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_RELATIVE_FILENAMES)
    {
      const char *path;

      iter = exts;
      g_assert_nonnull (iter);
      path = srt_egl_external_platform_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/not-a-file"));
      g_assert_true (g_path_is_absolute (path));
      assert_egl_external_platform_has_error (iter->data);
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_egl_external_platform_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/no-library.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/no-library.json", path);
      assert_egl_external_platform_has_error (iter->data);
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_EXPLICIT_FILENAMES)
    {
      iter = exts;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==, "/not-a-file");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==, "/no-library.json");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_null (iter);
    }
  else
    {
      /* EGL external platforms don't respect the XDG variables, so XDG_DIRS
       * is the same as NORMAL. */
      iter = exts;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/etc/egl/egl_external_platform.d/invalid.json");
      /* This one is invalid. */
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_egl_external_platform_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_egl_external_platform_get_json_path (iter->data), ==,
                       "/usr/share/egl/egl_external_platform.d/10_nvidia_wayland.json");
      assert_egl_external_platform_no_error (iter->data);
      g_assert_cmpstr (srt_egl_external_platform_get_library_path (iter->data), ==,
                       "libnvidia-egl-wayland.so.1");
      resolved = srt_egl_external_platform_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libnvidia-egl-wayland.so.1");
      g_assert_cmpint (srt_egl_external_platform_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_null (iter);
    }
}

/*
 * Assert that @icd is internally consistent.
 */
static void
assert_vulkan_icd (SrtVulkanIcd *icd)
{
  g_autoptr(GError) error = NULL;
  g_autoptr(GError) error_property = NULL;
  g_autofree gchar *api_version = NULL;
  g_autofree gchar *json_path = NULL;
  g_autofree gchar *library_path = NULL;
  g_autofree gchar *resolved = NULL;
  g_autofree gchar *resolved_property = NULL;

  g_assert_true (SRT_IS_VULKAN_ICD (icd));

  g_object_get (icd,
                "api-version", &api_version,
                "error", &error_property,
                "json-path", &json_path,
                "library-path", &library_path,
                "resolved-library-path", &resolved_property,
                NULL);
  resolved = srt_vulkan_icd_resolve_library_path (icd);

  g_assert_cmpstr (json_path, !=, NULL);
  g_assert_cmpstr (json_path, ==, srt_vulkan_icd_get_json_path (icd));
  g_assert_true (g_path_is_absolute (json_path));

  /* These are invariants, even if they're NULL */
  g_assert_cmpstr (api_version, ==, srt_vulkan_icd_get_api_version (icd));
  g_assert_cmpstr (library_path, ==, srt_vulkan_icd_get_library_path (icd));
  g_assert_cmpstr (resolved_property, ==, resolved);
  g_assert_cmpint (issues, ==, srt_vulkan_icd_get_issues (icd));

  if (error_property == NULL)
    {
      srt_vulkan_icd_check_error (icd, &error);
      g_assert_no_error (error);
      g_assert_true (srt_vulkan_icd_check_error (icd, NULL));
      g_assert_true (srt_vulkan_icd_check_error (icd, &error));
      g_assert_no_error (error);
      g_assert_no_error (error_property);
      g_assert_cmpstr (library_path, !=, NULL);
      g_assert_cmpstr (api_version, !=, NULL);
      g_assert_cmpstr (resolved, !=, NULL);
      g_assert_cmpstr (resolved_property, !=, NULL);

      if (strchr (resolved, '/') == NULL)
        {
          g_assert_cmpstr (resolved, ==, library_path);
        }
      else
        {
          g_assert_true (g_path_is_absolute (resolved));
        }
    }
  else
    {
      g_assert_false (srt_vulkan_icd_check_error (icd, NULL));
      g_assert_false (srt_vulkan_icd_check_error (icd, &error));
      g_assert_nonnull (error);
      g_assert_error (error, error_property->domain, error_property->code);
      g_assert_cmpstr (error->message, ==, error_property->message);
      g_assert_cmpstr (library_path, ==, NULL);
      g_assert_cmpstr (api_version, ==, NULL);
      g_assert_cmpstr (resolved, ==, NULL);
      g_assert_cmpstr (resolved_property, ==, NULL);
    }
}

/*
 * Assert that @icd is internally consistent and in a failed state.
 */
static void
assert_vulkan_icd_has_error (SrtVulkanIcd *icd)
{
  g_assert_false (srt_vulkan_icd_check_error (icd, NULL));
  assert_vulkan_icd (icd);
}

/*
 * Assert that @icd is internally consistent and in a successful state.
 */
static void
assert_vulkan_icd_no_error (SrtVulkanIcd *icd)
{
  GError *error = NULL;

  srt_vulkan_icd_check_error (icd, &error);
  g_assert_no_error (error);
  assert_vulkan_icd (icd);
}

static void
assert_vulkan_icds (const SrtObjectList *icds,
                    gconstpointer context)
{
  const Config *config = context;
  const GList *iter;
  g_autofree gchar *resolved = NULL;

  for (iter = icds; iter != NULL; iter = iter->next)
    {
      GError *error = NULL;

      g_test_message ("ICD: %s", srt_vulkan_icd_get_json_path (iter->data));

      if (srt_vulkan_icd_check_error (iter->data, &error))
        {
          g_test_message ("\tlibrary: %s",
                          srt_vulkan_icd_get_library_path (iter->data));
        }
      else
        {
          g_test_message ("\terror: %s", error->message);
          g_clear_error (&error);
        }
    }

  if (config != NULL && config->icd_mode == ICD_MODE_EXPLICIT_FILENAMES)
    {
      SrtVulkanIcd *other;

      /* We tried to add added.json via VK_ADD_DRIVER_FILES, but
       * VK_DRIVER_FILES "wins" and so VK_ADD_DRIVER_FILES is ignored */

      iter = icds;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/not-a-file");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      other = srt_vulkan_icd_new_replace_library_path (iter->data,
                                                       "/run/host/vulkan_icd.so");
      /* Copying an invalid ICD yields another invalid ICD. */
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);
      g_object_unref (other);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/null.json");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/false.json");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/str.json");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/no-library.json");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==, "/no-api-version.json");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/partial.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "libpartial.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==,
                       "1.2.101");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libpartial.so");
      g_clear_pointer (&resolved, g_free);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_API_SUBSET);

      iter = iter->next;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_RELATIVE_FILENAMES)
    {
      const char *path;

      /* We tried to add added.json via VK_ADD_DRIVER_FILES, but
       * VK_ICD_FILENAMES "wins" and so VK_ADD_DRIVER_FILES is ignored */

      iter = icds;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/not-a-file"));
      g_assert_true (g_path_is_absolute (path));
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/usr/share/vulkan/icd.d/intel_icd.x86_64.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/usr/share/vulkan/icd.d/intel_icd.x86_64.json", path);
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "/usr/lib/x86_64-mock-abi/libvulkan_intel.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/null.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/null.json", path);
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/false.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/false.json", path);
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/str.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/str.json", path);
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/no-library.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/no-library.json", path);
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      path = srt_vulkan_icd_get_json_path (iter->data);
      g_assert_true (g_str_has_suffix (path, "/fake-icds/no-api-version.json"));
      g_assert_true (g_path_is_absolute (path));
      assert_same_file ("fake-icds/no-api-version.json", path);
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_FLATPAK)
    {
      SrtVulkanIcd *other;

      iter = icds;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/etc/xdg/vulkan/icd.d/invalid.json");
      /* This is not valid JSON (it's an empty file) so loading it fails */
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/etc/vulkan/icd.d/basename.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "libvulkan_basename.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.2.3");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libvulkan_basename.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);
      g_free (resolved);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/lib/x86_64-mock-abi/GL/vulkan/icd.d/invalid.json");
      /* This has a JSON array, not an object, so loading it fails */
      assert_vulkan_icd_has_error (iter->data);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/lib/x86_64-mock-abi/vulkan/icd.d/relative.json");
      assert_vulkan_icd_no_error (iter->data);
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==,
                       "/usr/lib/x86_64-mock-abi/vulkan/icd.d/../libvulkan_relative.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_DUPLICATED);
      g_free (resolved);

      other = srt_vulkan_icd_new_replace_library_path (iter->data,
                                                       "/run/host/vulkan_icd.so");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (other), ==,
                       srt_vulkan_icd_get_json_path (iter->data));
      /* The pointers are not equal */
      g_assert_true (srt_vulkan_icd_get_json_path (other) !=
                     srt_vulkan_icd_get_json_path (iter->data));
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (other), ==,
                       srt_vulkan_icd_get_api_version (iter->data));
      /* The pointers are not equal */
      g_assert_true (srt_vulkan_icd_get_api_version (other) !=
                     srt_vulkan_icd_get_api_version (iter->data));
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (other), ==,
                       "/run/host/vulkan_icd.so");
      /* The %SRT_LOADABLE_ISSUES_DUPLICATED issues flag is preserved */
      g_assert_cmpint (srt_vulkan_icd_get_issues (other), ==,
                       SRT_LOADABLE_ISSUES_DUPLICATED);
      g_object_unref (other);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/home/.local/share/vulkan/icd.d/relative_new.json");
      assert_vulkan_icd_no_error (iter->data);
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==,
                       "/usr/lib/x86_64-mock-abi/vulkan/icd.d/../libvulkan_relative.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_DUPLICATED);
      g_free (resolved);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/local/share/vulkan/icd.d/intel_icd.i686.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "/usr/lib/i386-mock-abi/libvulkan_intel.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "/usr/lib/i386-mock-abi/libvulkan_intel.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/share/vulkan/icd.d/intel_icd.x86_64.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "/usr/lib/x86_64-mock-abi/libvulkan_intel.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_null (iter);
    }
  else if (config != NULL && config->icd_mode == ICD_MODE_XDG_DIRS)
    {
      iter = icds;
      /* Added via VK_ADD_DRIVER_FILES */
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/added.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "libadded.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==,
                       "1.1.102");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libadded.so");
      g_clear_pointer (&resolved, g_free);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      /* Vulkan-Loader >= 1.2.198 respects XDG_CONFIG_HOME */
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/confhome/vulkan/icd.d/invalid.json");
      /* Not format 1.0.x, so we can't be confident that we're reading
       * it correctly */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_UNSUPPORTED);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/confdir/vulkan/icd.d/invalid.json");
      /* Not format 1.0.x, so we can't be confident that we're reading
       * it correctly */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_UNSUPPORTED);

      iter = iter->next;
      g_assert_nonnull (iter);
      /* /etc is unaffected by XDG variables */
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/etc/vulkan/icd.d/basename.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "libvulkan_basename.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.2.3");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libvulkan_basename.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/datahome/vulkan/icd.d/invalid.json");
      /* Missing API version */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      /* We load $XDG_DATA_HOME *as well as* ~/.local/share.
       * This was originally based on a mis-reading of the reference
       * Vulkan loader, but it's desirable to keep this until
       * https://github.com/ValveSoftware/steam-for-linux/issues/8337
       * gets fixed. */
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/home/.local/share/vulkan/icd.d/invalid.json");
      /* This one lacks the required format version */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      /* We load $XDG_DATA_DIRS instead of /usr/local/share:/usr/share.
       * In this case it only has one item. */
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/datadir/vulkan/icd.d/invalid.json");
      /* Not format 1.0.x, so we can't be confident that we're reading
       * it correctly */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_UNSUPPORTED);

      iter = iter->next;
      g_assert_null (iter);
    }
  else
    {
      iter = icds;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/home/.config/vulkan/icd.d/invalid.json");
      /* This one lacks the required format version */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/etc/xdg/vulkan/icd.d/invalid.json");
      /* This is not valid JSON (it's an empty file) so loading it fails */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/etc/vulkan/icd.d/basename.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "libvulkan_basename.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.2.3");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "libvulkan_basename.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);
      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/home/.local/share/vulkan/icd.d/invalid.json");
      /* This one lacks the required format version */
      assert_vulkan_icd_has_error (iter->data);
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_CANNOT_LOAD);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/local/share/vulkan/icd.d/intel_icd.i686.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "/usr/lib/i386-mock-abi/libvulkan_intel.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
      resolved = srt_vulkan_icd_resolve_library_path (iter->data);
      g_assert_cmpstr (resolved, ==, "/usr/lib/i386-mock-abi/libvulkan_intel.so");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_nonnull (iter);
      g_assert_cmpstr (srt_vulkan_icd_get_json_path (iter->data), ==,
                       "/usr/share/vulkan/icd.d/intel_icd.x86_64.json");
      assert_vulkan_icd_no_error (iter->data);
      g_assert_cmpstr (srt_vulkan_icd_get_library_path (iter->data), ==,
                       "/usr/lib/x86_64-mock-abi/libvulkan_intel.so");
      g_assert_cmpstr (srt_vulkan_icd_get_api_version (iter->data), ==, "1.1.102");
      g_assert_cmpint (srt_vulkan_icd_get_issues (iter->data), ==,
                       SRT_LOADABLE_ISSUES_NONE);

      iter = iter->next;
      g_assert_null (iter);
    }
}

static void
test_icd_vulkan_explicit_multiarch (Fixture *f,
                                    gconstpointer context)
{
  const Config *config = context;
  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
  g_autoptr(SrtObjectList) icds = NULL;
  const char * const multiarchs[] = { "x86_64-mock-abi", NULL };

  g_test_message ("Entering %s", G_STRFUNC);

  if (config != NULL)
    g_test_message ("Mode: %s", icd_mode_to_string (config->icd_mode));

  if (f->sysroot != NULL)
    g_test_message ("Sysroot: %s", glnx_basename (f->sysroot));

  srt_system_info_set_environ (info, f->fake_icds_envp);
  srt_system_info_set_sysroot (info, f->sysroot);
  srt_system_info_set_helpers_path (info, f->builddir);

  icds = srt_system_info_list_vulkan_icds (info, multiarchs);

  assert_vulkan_icds (icds, context);
}

static void
test_icd_vulkan_implicit_multiarch (Fixture *f,
                                   gconstpointer context)
{
  const Config *config = context;
  g_autoptr(SrtSystemInfo) info = srt_system_info_new (NULL);
  g_autoptr(SrtObjectList) icds = NULL;
  const char * const multiarchs[] = { "x86_64-mock-abi", NULL };

  g_test_message ("Entering %s", G_STRFUNC);

  if (config != NULL)
    g_test_message ("Mode: %s", icd_mode_to_string (config->icd_mode));

  if (f->sysroot != NULL)
    g_test_message ("Sysroot: %s", glnx_basename (f->sysroot));

  srt_system_info_set_environ (info, f->fake_icds_envp);
  srt_system_info_set_sysroot (info, f->sysroot);
  srt_system_info_set_helpers_path (info, f->builddir);
  srt_system_info_set_multiarch_tuples (info, multiarchs);

  icds = srt_system_info_list_vulkan_icds (info, NULL);

  assert_vulkan_icds (icds, context);
}

typedef struct
{
  const gchar *name;
  const gchar *description;
  const gchar *library_path;
  const gchar *api_version;
  const gchar *component_layers[5];
  /* This needs to be an explicit value because, if in input we had a single
   * JSON with multiple layers, we write it to the filesystem as separated JSON
   * files. So the output is not always exactly the same as the input JSON */
  const gchar *json_to_compare;
  const gchar *error_message_suffix;
  const gchar *error_domain;
  gint error_code;
} VulkanLayerTest;

typedef struct
{
  const gchar *description;
  const gchar *sysroot;
  const gchar *vk_add_layer_path;
  const gchar *vk_layer_path;
  const gchar *home;
  const gchar *xdg_config_dirs;
  const gchar *xdg_data_dirs;
  VulkanLayerTest explicit_layers[5];
  VulkanLayerTest implicit_layers[5];
} VulkanLayersTest;

static const VulkanLayersTest vulkan_layers_test[] =
{
  {
    .description = "Good single VK_LAYER_PATH dir",
    .sysroot = "debian10",
    .vk_layer_path = "/custom_path",
    .explicit_layers =
    {
      {
        .name = "VK_LAYER_MANGOHUD_overlay",
        .description = "Vulkan Hud Overlay",
        .library_path = "/usr/$LIB/libMangoHud.so",
        .api_version = "1.2.135",
        .json_to_compare = "expectations/MangoHud.json",
      },
      {
        .name = "VK_LAYER_LUNARG_overlay",
        .description = "LunarG HUD layer",
        .library_path = "vkOverlayLayer.so",
        .api_version = "1.1.5",
        .json_to_compare = "custom_path/Single-good-layer.json",
      },
    },
    /* Implicit layers are not affected by VK_LAYER_PATH env */
    .implicit_layers =
    {
      {
        .name = "VK_LAYER_first",
        .description = "Vulkan first layer",
        .library_path = "libFirst.so",
        .api_version = "1.0.13",
        .json_to_compare = "expectations/MultiLayers_part1.json",
      },
      {
        .name = "VK_LAYER_second",
        .description = "Vulkan second layer",
        .library_path = "libSecond.so",
        .api_version = "1.0.13",
        .json_to_compare = "expectations/MultiLayers_part2.json",
      },
    },
  },

  {
    .description = "Good implicit dirs",
    .sysroot = "debian10",
    .home = "/home/debian",
    .xdg_config_dirs = "/usr/local/etc:::",
    .vk_add_layer_path = "/custom_path",
    .explicit_layers =
    {
      /* VK_ADD_LAYER_PATH is searched before the default search path */
      {
        .name = "VK_LAYER_MANGOHUD_overlay",
        .description = "Vulkan Hud Overlay",
        .api_version = "1.2.135",
        .library_path = "/usr/$LIB/libMangoHud.so",
        .json_to_compare = "expectations/MangoHud.json",
      },
      {
        .name = "VK_LAYER_LUNARG_overlay",
        .description = "LunarG HUD layer",
        .api_version = "1.1.5",
        .library_path = "vkOverlayLayer.so",
        .json_to_compare = "custom_path/Single-good-layer.json",
      },
      {
        .name = "VK_LAYER_MESA_overlay",
        .description = "Mesa Overlay layer",
        .library_path = "libVkLayer_MESA_overlay.so",
        .api_version = "1.1.73",
        .json_to_compare = "usr/local/etc/vulkan/explicit_layer.d/VkLayer_MESA_overlay.json",
      },
    },
    .implicit_layers =
    {
      {
        .name = "VK_LAYER_VALVE_steam_overlay_64",
        .description = "Steam Overlay Layer",
        .library_path = "/home/debian/.local/share/Steam/ubuntu12_64/steamoverlayvulkanlayer.so",
        .api_version = "1.2.136",
        .json_to_compare = "home/debian/.local/share/vulkan/implicit_layer.d/steamoverlay_x86_64.json",
      },
      {
        .name = "VK_LAYER_first",
        .description = "Vulkan first layer",
        .library_path = "libFirst.so",
        .api_version = "1.0.13",
        .json_to_compare = "expectations/MultiLayers_part1.json",
      },
      {
        .name = "VK_LAYER_second",
        .description = "Vulkan second layer",
        .library_path = "libSecond.so",
        .api_version = "1.0.13",
        .json_to_compare = "expectations/MultiLayers_part2.json",
      },
    },
  },

  {
    .description = "Layers with missing required fields and unsupported version",
    .sysroot = "fedora",
    .implicit_layers =
    {
      // incomplete_layer.json
      {
        .issues = SRT_LOADABLE_ISSUES_CANNOT_LOAD,
        .error_message_suffix = "cannot be parsed because it is missing a required field",
        .error_domain = "g-io-error-quark",
        .error_code = G_IO_ERROR_FAILED,
      },
      // newer_layer.json
      {
        .issues = SRT_LOADABLE_ISSUES_UNSUPPORTED,
        .error_message_suffix = "is not supported",
        .error_domain = "g-io-error-quark",
        .error_code = G_IO_ERROR_FAILED,
      },
    },
  },

  {
    .description = "Meta layer",
    .sysroot = "fedora",
    .vk_layer_path = "/custom_path:/custom_path2:/custom_path3",
    /* /usr/local/etc is not searched because VK_LAYER_PATH "wins" */
    .vk_add_layer_path = "/usr/local/etc",
    .explicit_layers =
    {
      {
        .name = "VK_LAYER_META_layer",
        .description = "Meta-layer example",
        .api_version = "1.0.9000",
        .component_layers =
        {
          "VK_LAYER_KHRONOS_validation",
          "VK_LAYER_LUNARG_api_dump",
        },
        .json_to_compare = "custom_path/meta_layer.json",
      },
      {
        .name = "VK_LAYER_MANGOHUD_overlay",
        .description = "Vulkan Hud Overlay",
        .library_path = "/usr/$LIB/libMangoHud.so",
        .api_version = "1.2.135",
        .json_to_compare = "custom_path2/MangoHud.json",
        .issues = SRT_LOADABLE_ISSUES_DUPLICATED,
      },
      {
        .name = "VK_LAYER_MANGOHUD_overlay",
        .description = "Vulkan Hud Overlay",
        .library_path = "/usr/lib32/libMangoHud.so",
        .api_version = "1.2.135",
        .json_to_compare = "custom_path3/MangoHud_i386.json",
        .issues = SRT_LOADABLE_ISSUES_DUPLICATED,
      },
    },
    .implicit_layers =
    {
      // incomplete_layer.json
      {
        .issues = SRT_LOADABLE_ISSUES_CANNOT_LOAD,
        .error_message_suffix = "cannot be parsed because it is missing a required field",
        .error_domain = "g-io-error-quark",
        .error_code = G_IO_ERROR_FAILED,
      },
      // newer_layer.json
      {
        .issues = SRT_LOADABLE_ISSUES_UNSUPPORTED,
        .error_message_suffix = "is not supported",
        .error_domain = "g-io-error-quark",
        .error_code = G_IO_ERROR_FAILED,
      },
  },

  {
    .description = "Special case for Flatpak",
    .sysroot = "fake-icds-flatpak",
    .explicit_layers =
    {
      /* /usr/lib/x86_64-mock-abi/GL/vulkan/explicit_layer.d/glext.json */
      {
        .name = "VK_LAYER_GLEXT_explicit",
        .description = "GL extension's explicit layer",
        .library_path = "libVkLayer_GLEXT_explicit.so",
        .api_version = "1.1.73",