1-module(couch_replicator_test_helper). 2 3-include_lib("couch/include/couch_eunit.hrl"). 4-include_lib("couch/include/couch_db.hrl"). 5-include_lib("couch_replicator/src/couch_replicator.hrl"). 6 7-export([ 8 compare_dbs/2, 9 compare_dbs/3, 10 db_url/1, 11 replicate/1, 12 get_pid/1, 13 replicate/2 14]). 15 16 17compare_dbs(Source, Target) -> 18 compare_dbs(Source, Target, []). 19 20 21compare_dbs(Source, Target, ExceptIds) -> 22 {ok, SourceDb} = couch_db:open_int(Source, []), 23 {ok, TargetDb} = couch_db:open_int(Target, []), 24 25 Fun = fun(FullDocInfo, Acc) -> 26 {ok, DocSource} = couch_db:open_doc(SourceDb, FullDocInfo), 27 Id = DocSource#doc.id, 28 case lists:member(Id, ExceptIds) of 29 true -> 30 ?assertEqual(not_found, couch_db:get_doc_info(TargetDb, Id)); 31 false -> 32 {ok, TDoc} = couch_db:open_doc(TargetDb, Id), 33 compare_docs(DocSource, TDoc) 34 end, 35 {ok, Acc} 36 end, 37 38 {ok, _} = couch_db:fold_docs(SourceDb, Fun, [], []), 39 ok = couch_db:close(SourceDb), 40 ok = couch_db:close(TargetDb). 41 42 43compare_docs(Doc1, Doc2) -> 44 ?assertEqual(Doc1#doc.body, Doc2#doc.body), 45 #doc{atts = Atts1} = Doc1, 46 #doc{atts = Atts2} = Doc2, 47 ?assertEqual(lists:sort([couch_att:fetch(name, Att) || Att <- Atts1]), 48 lists:sort([couch_att:fetch(name, Att) || Att <- Atts2])), 49 FunCompareAtts = fun(Att) -> 50 AttName = couch_att:fetch(name, Att), 51 {ok, AttTarget} = find_att(Atts2, AttName), 52 SourceMd5 = att_md5(Att), 53 TargetMd5 = att_md5(AttTarget), 54 case AttName of 55 <<"att1">> -> 56 ?assertEqual(gzip, couch_att:fetch(encoding, Att)), 57 ?assertEqual(gzip, couch_att:fetch(encoding, AttTarget)), 58 DecSourceMd5 = att_decoded_md5(Att), 59 DecTargetMd5 = att_decoded_md5(AttTarget), 60 ?assertEqual(DecSourceMd5, DecTargetMd5); 61 _ -> 62 ?assertEqual(identity, couch_att:fetch(encoding, AttTarget)), 63 ?assertEqual(identity, couch_att:fetch(encoding, AttTarget)) 64 end, 65 ?assertEqual(SourceMd5, TargetMd5), 66 ?assert(is_integer(couch_att:fetch(disk_len, Att))), 67 ?assert(is_integer(couch_att:fetch(att_len, Att))), 68 ?assert(is_integer(couch_att:fetch(disk_len, AttTarget))), 69 ?assert(is_integer(couch_att:fetch(att_len, AttTarget))), 70 ?assertEqual(couch_att:fetch(disk_len, Att), 71 couch_att:fetch(disk_len, AttTarget)), 72 ?assertEqual(couch_att:fetch(att_len, Att), 73 couch_att:fetch(att_len, AttTarget)), 74 ?assertEqual(couch_att:fetch(type, Att), 75 couch_att:fetch(type, AttTarget)), 76 ?assertEqual(couch_att:fetch(md5, Att), 77 couch_att:fetch(md5, AttTarget)) 78 end, 79 lists:foreach(FunCompareAtts, Atts1). 80 81 82find_att([], _Name) -> 83 nil; 84find_att([Att | Rest], Name) -> 85 case couch_att:fetch(name, Att) of 86 Name -> 87 {ok, Att}; 88 _ -> 89 find_att(Rest, Name) 90 end. 91 92 93att_md5(Att) -> 94 Md50 = couch_att:foldl( 95 Att, 96 fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end, 97 couch_hash:md5_hash_init()), 98 couch_hash:md5_hash_final(Md50). 99 100att_decoded_md5(Att) -> 101 Md50 = couch_att:foldl_decode( 102 Att, 103 fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end, 104 couch_hash:md5_hash_init()), 105 couch_hash:md5_hash_final(Md50). 106 107db_url(DbName) -> 108 iolist_to_binary([ 109 "http://", config:get("httpd", "bind_address", "127.0.0.1"), 110 ":", integer_to_list(mochiweb_socket_server:get(couch_httpd, port)), 111 "/", DbName 112 ]). 113 114get_pid(RepId) -> 115 Pid = global:whereis_name({couch_replicator_scheduler_job,RepId}), 116 ?assert(is_pid(Pid)), 117 Pid. 118 119replicate(Source, Target) -> 120 replicate({[ 121 {<<"source">>, Source}, 122 {<<"target">>, Target} 123 ]}). 124 125replicate({[_ | _]} = RepObject) -> 126 {ok, Rep} = couch_replicator_utils:parse_rep_doc(RepObject, ?ADMIN_USER), 127 ok = couch_replicator_scheduler:add_job(Rep), 128 couch_replicator_scheduler:reschedule(), 129 Pid = get_pid(Rep#rep.id), 130 MonRef = erlang:monitor(process, Pid), 131 receive 132 {'DOWN', MonRef, process, Pid, _} -> 133 ok 134 end, 135 ok = couch_replicator_scheduler:remove_job(Rep#rep.id). 136