# HG changeset patch
# Node ID b18f43642f767c5ba8dcced080bf74fb7b22e0c1
# Parent  595a5f31003a2899534d24cc655ef757ec97d4db
diff --git a/libpurple/purplebadges.c b/libpurple/purplebadges.c
--- a/libpurple/purplebadges.c
+++ b/libpurple/purplebadges.c
@@ -52,7 +52,7 @@
 }
 
 static int
-purple_badges_compare(gconstpointer a, gconstpointer b) {
+purple_badges_compare_badges(gconstpointer a, gconstpointer b) {
 	PurpleBadge *badge1 = (gpointer)a;
 	PurpleBadge *badge2 = (gpointer)b;
 
@@ -174,11 +174,6 @@
 /******************************************************************************
  * Public API
  *****************************************************************************/
-PurpleBadges *
-purple_badges_new(void) {
-	return g_object_new(PURPLE_TYPE_BADGES, NULL);
-}
-
 gboolean
 purple_badges_add_badge(PurpleBadges *badges, PurpleBadge *badge) {
 	gboolean found = FALSE;
@@ -198,7 +193,7 @@
 	len = badges->badges->len;
 
 	g_ptr_array_add(badges->badges, g_object_ref(badge));
-	g_ptr_array_sort_values(badges->badges, purple_badges_compare);
+	g_ptr_array_sort_values(badges->badges, purple_badges_compare_badges);
 
 	g_list_model_items_changed(G_LIST_MODEL(badges), 0, len, len + 1);
 	g_object_notify_by_pspec(G_OBJECT(badges), properties[PROP_N_ITEMS]);
@@ -206,6 +201,59 @@
 	return TRUE;
 }
 
+int
+purple_badges_compare(PurpleBadges *badges1, PurpleBadges *badges2) {
+	guint n_badges1 = 0;
+	guint n_badges2 = 0;
+	guint n_badges = 0;
+
+	/* NULL checks. */
+	if(badges1 == badges2) {
+		return 0;
+	}
+
+	if(PURPLE_IS_BADGES(badges1) && !PURPLE_IS_BADGES(badges2)) {
+		return -1;
+	}
+
+	if(PURPLE_IS_BADGES(badges2) && !PURPLE_IS_BADGES(badges1)) {
+		return 1;
+	}
+
+	n_badges1 = badges1->badges->len;
+	n_badges2 = badges2->badges->len;
+
+	/* Empty checks. */
+	if(n_badges1 > 0 && n_badges2 == 0) {
+		return -1;
+	}
+
+	if(n_badges1 == 0 && n_badges2 > 0) {
+		return 1;
+	}
+
+	/* We now have to walk through and compare badges. */
+	n_badges = MIN(n_badges1, n_badges2);
+	for(guint i = 0; i < n_badges; i++) {
+		PurpleBadge *badge1 = g_ptr_array_index(badges1->badges, i);
+		PurpleBadge *badge2 = g_ptr_array_index(badges2->badges, i);
+		guint result = 0;
+
+		result = purple_badge_compare(badge1, badge2);
+		if(result != 0) {
+			return result;
+		}
+	}
+
+	/* All badges were equal so fall back to which one has the most badges. */
+	return n_badges2 - n_badges1;
+}
+
+PurpleBadges *
+purple_badges_new(void) {
+	return g_object_new(PURPLE_TYPE_BADGES, NULL);
+}
+
 gboolean
 purple_badges_remove_badge(PurpleBadges *badges, const char *id) {
 	PurpleBadge *needle = NULL;
diff --git a/libpurple/purplebadges.h b/libpurple/purplebadges.h
--- a/libpurple/purplebadges.h
+++ b/libpurple/purplebadges.h
@@ -52,18 +52,6 @@
 G_DECLARE_FINAL_TYPE(PurpleBadges, purple_badges, PURPLE, BADGES, GObject)
 
 /**
- * purple_badges_new:
- *
- * Creates a new badges collection.
- *
- * Returns: (transfer full): The new instance.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-PurpleBadges *purple_badges_new(void);
-
-/**
  * purple_badges_add_badge:
  * @badges: The instance.
  * @badge: The new badge to add.
@@ -82,6 +70,38 @@
 gboolean purple_badges_add_badge(PurpleBadges *badges, PurpleBadge *badge);
 
 /**
+ * purple_badges_compare:
+ * @badges2: (nullable): the instance to compare
+ *
+ * Compares two badges instances for sorting.
+ *
+ * The sort order for badges is based on the priority of the badges they
+ * contain with the highest priority sorting first. If the priorities are equal
+ * then the next badge is tested continuing until a difference is found or all
+ * badges have been exhausted.
+ *
+ * Returns: less than zero if the first badge should be sorted first, 0 if they
+ *          are equal, greater than 0 if the second badge should be sorted
+ *          first.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+int purple_badges_compare(PurpleBadges *badges1, PurpleBadges *badges2);
+
+/**
+ * purple_badges_new:
+ *
+ * Creates a new badges collection.
+ *
+ * Returns: (transfer full): The new instance.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleBadges *purple_badges_new(void);
+
+/**
  * purple_badges_remove_badge:
  * @badges: The instance.
  * @id: The id of the badge to remove.
diff --git a/libpurple/tests/test_badges.c b/libpurple/tests/test_badges.c
--- a/libpurple/tests/test_badges.c
+++ b/libpurple/tests/test_badges.c
@@ -247,6 +247,206 @@
 }
 
 /******************************************************************************
+ * Compare Tests
+ *****************************************************************************/
+static void
+test_purple_badges_compare_null__null(void) {
+	int result = 0;
+
+	result = purple_badges_compare(NULL, NULL);
+	g_assert_cmpint(result, ==, 0);
+}
+
+static void
+test_purple_badges_compare_empty__null(void) {
+	PurpleBadges *badges = NULL;
+	int result = 0;
+
+	badges = purple_badges_new();
+
+	result = purple_badges_compare(badges, NULL);
+	g_assert_cmpint(result, <, 0);
+
+	g_assert_finalize_object(badges);
+}
+
+static void
+test_purple_badges_compare_null__empty(void) {
+	PurpleBadges *badges = NULL;
+	int result = 0;
+
+	badges = purple_badges_new();
+
+	result = purple_badges_compare(NULL, badges);
+	g_assert_cmpint(result, >, 0);
+
+	g_assert_finalize_object(badges);
+}
+
+static void
+test_purple_badges_compare_empty__empty(void) {
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badges2 = purple_badges_new();
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, ==, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_single__empty(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badge = purple_badge_new("test", 1000, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+
+	badges2 = purple_badges_new();
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, <, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_empty__single(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+
+	badges2 = purple_badges_new();
+	badge = purple_badge_new("test", 1000, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, >, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_multiple__single__left(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badge = purple_badge_new("test1", 1, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+	badge = purple_badge_new("test2", 1000, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+
+	badges2 = purple_badges_new();
+	badge = purple_badge_new("test3", 1000, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, <, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_multiple__single__right(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badge = purple_badge_new("test1", 1, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+	badge = purple_badge_new("test2", 2, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+
+	badges2 = purple_badges_new();
+	badge = purple_badge_new("test3", 1000, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, >, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_single__multiple__right(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badge = purple_badge_new("test1", 500, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+
+	badges2 = purple_badges_new();
+	badge = purple_badge_new("test2", 500, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+	badge = purple_badge_new("test3", 1000, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, >, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+static void
+test_purple_badges_compare_single__single__equal(void) {
+	PurpleBadge *badge = NULL;
+	PurpleBadges *badges1 = NULL;
+	PurpleBadges *badges2 = NULL;
+	int result = 0;
+
+	badges1 = purple_badges_new();
+	badge = purple_badge_new("test1", 500, "icon", "t");
+	purple_badges_add_badge(badges1, badge);
+	g_clear_object(&badge);
+
+	badges2 = purple_badges_new();
+	badge = purple_badge_new("test2", 500, "icon", "t");
+	purple_badges_add_badge(badges2, badge);
+	g_clear_object(&badge);
+
+	result = purple_badges_compare(badges1, badges2);
+	g_assert_cmpint(result, ==, 0);
+
+	g_assert_finalize_object(badges1);
+	g_assert_finalize_object(badges2);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 int
@@ -259,5 +459,26 @@
 	g_test_add_func("/badges/double-add", test_purple_badges_double_add);
 	g_test_add_func("/badges/double-remove", test_purple_badges_double_remove);
 
+	g_test_add_func("/badges/compare/null--null",
+	                test_purple_badges_compare_null__null);
+	g_test_add_func("/badges/compare/empty--null",
+	                test_purple_badges_compare_empty__null);
+	g_test_add_func("/badges/compare/null--empty",
+	                test_purple_badges_compare_null__empty);
+	g_test_add_func("/badges/compare/empty--empty",
+	                test_purple_badges_compare_empty__empty);
+	g_test_add_func("/badges/compare/single--empty",
+	                test_purple_badges_compare_single__empty);
+	g_test_add_func("/badges/compare/empty--single",
+	                test_purple_badges_compare_empty__single);
+	g_test_add_func("/badges/compare/multiple--single--left",
+	                test_purple_badges_compare_multiple__single__left);
+	g_test_add_func("/badges/compare/multiple--single--right",
+	                test_purple_badges_compare_multiple__single__right);
+	g_test_add_func("/badges/compare/single--multiple--right",
+	                test_purple_badges_compare_single__multiple__right);
+	g_test_add_func("/badges/compare/single--single--equal",
+	                test_purple_badges_compare_single__single__equal);
+
 	return g_test_run();
 }
