patch 8.2.0969: assert_equal() output for dicts is hard to figure out

Problem:    Assert_equal() output for dicts is hard to figure out.
Solution:   Only show the different items.
diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim
index 2147e3d..c2d055c 100644
--- a/src/testdir/test_assert.vim
+++ b/src/testdir/test_assert.vim
@@ -50,6 +50,26 @@
   call remove(v:errors, 0)
 endfunc
 
+func Test_assert_equal_dict()
+  call assert_equal(0, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 1}))
+
+  call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 3}))
+  call assert_match("Expected {'one': 1} but got {'one': 3} - 1 equal item omitted", v:errors[0])
+  call remove(v:errors, 0)
+
+  call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 22, one: 11}))
+  call assert_match("Expected {'one': 1, 'two': 2} but got {'one': 11, 'two': 22}", v:errors[0])
+  call remove(v:errors, 0)
+
+  call assert_equal(1, assert_equal(#{}, #{two: 2, one: 1}))
+  call assert_match("Expected {} but got {'one': 1, 'two': 2}", v:errors[0])
+  call remove(v:errors, 0)
+
+  call assert_equal(1, assert_equal(#{two: 2, one: 1}, #{}))
+  call assert_match("Expected {'one': 1, 'two': 2} but got {}", v:errors[0])
+  call remove(v:errors, 0)
+endfunc
+
 func Test_assert_equalfile()
   call assert_equal(1, assert_equalfile('abcabc', 'xyzxyz'))
   call assert_match("E485: Can't read file abcabc", v:errors[0])
diff --git a/src/testing.c b/src/testing.c
index 0eee72a..c01ae3d 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -131,12 +131,16 @@
     garray_T	*gap,
     typval_T	*opt_msg_tv,
     char_u      *exp_str,
-    typval_T	*exp_tv,
-    typval_T	*got_tv,
+    typval_T	*exp_tv_arg,
+    typval_T	*got_tv_arg,
     assert_type_T atype)
 {
     char_u	numbuf[NUMBUFLEN];
     char_u	*tofree;
+    typval_T	*exp_tv = exp_tv_arg;
+    typval_T	*got_tv = got_tv_arg;
+    int		did_copy = FALSE;
+    int		omitted = 0;
 
     if (opt_msg_tv->v_type != VAR_UNKNOWN)
     {
@@ -153,6 +157,62 @@
 	ga_concat(gap, (char_u *)"Expected ");
     if (exp_str == NULL)
     {
+	// When comparing dictionaries, drop the items that are equal, so that
+	// it's a lot easier to see what differs.
+	if (atype != ASSERT_NOTEQUAL
+		&& exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT
+		&& exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL)
+	{
+	    dict_T	*exp_d = exp_tv->vval.v_dict;
+	    dict_T	*got_d = got_tv->vval.v_dict;
+	    hashitem_T	*hi;
+	    dictitem_T	*item2;
+	    int		todo;
+
+	    did_copy = TRUE;
+	    exp_tv->vval.v_dict = dict_alloc();
+	    got_tv->vval.v_dict = dict_alloc();
+	    if (exp_tv->vval.v_dict == NULL || got_tv->vval.v_dict == NULL)
+		return;
+
+	    todo = (int)exp_d->dv_hashtab.ht_used;
+	    for (hi = exp_d->dv_hashtab.ht_array; todo > 0; ++hi)
+	    {
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    item2 = dict_find(got_d, hi->hi_key, -1);
+		    if (item2 == NULL || !tv_equal(&HI2DI(hi)->di_tv,
+						  &item2->di_tv, FALSE, FALSE))
+		    {
+			// item of exp_d not present in got_d or values differ.
+			dict_add_tv(exp_tv->vval.v_dict,
+					(char *)hi->hi_key, &HI2DI(hi)->di_tv);
+			if (item2 != NULL)
+			    dict_add_tv(got_tv->vval.v_dict,
+					    (char *)hi->hi_key, &item2->di_tv);
+		    }
+		    else
+			++omitted;
+		    --todo;
+		}
+	    }
+
+	    // Add items only present in got_d.
+	    todo = (int)got_d->dv_hashtab.ht_used;
+	    for (hi = got_d->dv_hashtab.ht_array; todo > 0; ++hi)
+	    {
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    item2 = dict_find(exp_d, hi->hi_key, -1);
+		    if (item2 == NULL)
+			// item of got_d not present in exp_d
+			dict_add_tv(got_tv->vval.v_dict,
+					(char *)hi->hi_key, &HI2DI(hi)->di_tv);
+		    --todo;
+		}
+	    }
+	}
+
 	ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0));
 	vim_free(tofree);
     }
@@ -168,6 +228,21 @@
 	    ga_concat(gap, (char_u *)" but got ");
 	ga_concat_shorten_esc(gap, tv2string(got_tv, &tofree, numbuf, 0));
 	vim_free(tofree);
+
+	if (omitted != 0)
+	{
+	    char buf[100];
+
+	    vim_snprintf(buf, 100, " - %d equal item%s omitted",
+					     omitted, omitted == 1 ? "" : "s");
+	    ga_concat(gap, (char_u *)buf);
+	}
+    }
+
+    if (did_copy)
+    {
+	clear_tv(exp_tv);
+	clear_tv(got_tv);
     }
 }
 
diff --git a/src/version.c b/src/version.c
index fa44d04..1c660ed 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    969,
+/**/
     968,
 /**/
     967,