Rework to support equivalent-but-not-equal keys

The range collation test would fail when presented with a set of keys
that compare equal according to ICU but not according to Erlang's =:=
operator.

Other todos here include testing the raw collator and ensuring that all
tests use the correct collator (most of the other ones are also
technically incorrect but we just don't see it with the choices of data
involved). Taking care of this incrementally rather than rewriting the
whole module.
diff --git a/test/couch_mrview_collation_tests.erl b/test/couch_mrview_collation_tests.erl
index df06ad6..4a03359 100644
--- a/test/couch_mrview_collation_tests.erl
+++ b/test/couch_mrview_collation_tests.erl
@@ -34,6 +34,12 @@
     <<"ba">>,
     <<"bb">>,
 
+    % U+200B is a zero-width space, which will be ignored by ICU but will cause
+    % the raw collator to treat these as three distinct keys
+    <<"c">>,
+    unicode:characters_to_binary([$c, 16#200B]),
+    unicode:characters_to_binary([$c, 16#200B, 16#200B]),
+
     [<<"a">>],
     [<<"b">>],
     [<<"b">>, <<"c">>],
@@ -73,7 +79,7 @@
                 [
                     fun should_collate_fwd/1,
                     fun should_collate_rev/1,
-                    fun should_collate_range/1,
+                    fun should_collate_range_/1,
                     fun should_collate_with_inclusive_end_fwd/1,
                     fun should_collate_with_inclusive_end_rev/1,
                     fun should_collate_without_inclusive_end_fwd/1,
@@ -95,23 +101,30 @@
     Expect = [{meta, [{total, length(?VALUES)}, {offset, 0}]}] ++ lists:reverse(rows()),
     ?_assertEquiv(Expect, Results).
 
-should_collate_range(Db) ->
-    ?_assertNot(
-        begin
-            {_, Error} = lists:foldl(fun(V, {Count, Error}) ->
-                {ok, Results} = run_query(Db, [{start_key, V}, {end_key, V}]),
-                Id = list_to_binary(integer_to_list(Count)),
-                Expect = [
-                    {meta, [{total, length(?VALUES)}, {offset, Count}]},
-                    {row, [{id, Id}, {key, V}, {value, 0}]}
-                ],
-                case Results == Expect of
-                    true -> {Count+1, Error};
-                    _ -> {Count+1, true}
-                end
-            end, {0, false}, ?VALUES),
-            Error
-        end).
+should_collate_range_(Db) ->
+    Index = lists:zip(lists:seq(0, length(?VALUES)-1), ?VALUES),
+    lists:map(fun(V) ->
+        {ok, Results} = run_query(Db, [{start_key, V}, {end_key, V}]),
+        Expect = [
+            {meta, [{total, length(?VALUES)}, find_offset(Index, V)]} |
+            find_matching_rows(Index, V)
+        ],
+        ?_assertEquiv(Expect, Results)
+    end, ?VALUES).
+
+find_offset(Index, Value) ->
+    [{Offset, _} | _] = lists:dropwhile(fun({_, V}) ->
+        couch_ejson_compare:less(Value, V) =/= 0
+    end, Index),
+    {offset, Offset}.
+
+find_matching_rows(Index, Value) ->
+    Matches = lists:filter(fun({_, V}) ->
+        couch_ejson_compare:less(Value, V) =:= 0
+    end, Index),
+    lists:map(fun({Id, V}) ->
+        {row, [{id, list_to_binary(integer_to_list(Id))}, {key, V}, {value, 0}]}
+    end, Matches).
 
 should_collate_with_inclusive_end_fwd(Db) ->
     Opts = [{end_key, <<"b">>}, {inclusive_end, true}],