couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rnew...@apache.org
Subject [01/12] Replace ejson with jiffy
Date Mon, 23 Dec 2013 23:31:10 GMT
Updated Branches:
  refs/heads/1843-replace-ejson-with-jiffy [created] 2e6092e4e


http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/simple.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/simple.json b/src/jiffy/test/cases/simple.json
new file mode 100644
index 0000000..9ed80c9
--- /dev/null
+++ b/src/jiffy/test/cases/simple.json
@@ -0,0 +1,5 @@
+{
+  "this": "is",
+  "really": "simple",
+  "json": "right?"
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_invalid_escape.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_invalid_escape.eterm b/src/jiffy/test/cases/string_invalid_escape.eterm
new file mode 100644
index 0000000..eb20890
--- /dev/null
+++ b/src/jiffy/test/cases/string_invalid_escape.eterm
@@ -0,0 +1 @@
+{error,{63,invalid_string}}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_invalid_escape.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_invalid_escape.json b/src/jiffy/test/cases/string_invalid_escape.json
new file mode 100644
index 0000000..54a38ac
--- /dev/null
+++ b/src/jiffy/test/cases/string_invalid_escape.json
@@ -0,0 +1 @@
+["\n foo \/ bar \r\f\\\ufffd\t\b\"\\ and you can't escape thi\s"]

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_invalid_hex_char.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_invalid_hex_char.eterm b/src/jiffy/test/cases/string_invalid_hex_char.eterm
new file mode 100644
index 0000000..51de423
--- /dev/null
+++ b/src/jiffy/test/cases/string_invalid_hex_char.eterm
@@ -0,0 +1 @@
+{error,{44,invalid_string}}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_invalid_hex_char.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_invalid_hex_char.json b/src/jiffy/test/cases/string_invalid_hex_char.json
new file mode 100644
index 0000000..bde7ee9
--- /dev/null
+++ b/src/jiffy/test/cases/string_invalid_hex_char.json
@@ -0,0 +1 @@
+"foo foo, blah blah \u0123 \u4567 \u89ab \uc/ef \uABCD \uEFFE bar baz bing"

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_with_escapes.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_with_escapes.eterm b/src/jiffy/test/cases/string_with_escapes.eterm
new file mode 100644
index 0000000..51f7f25
--- /dev/null
+++ b/src/jiffy/test/cases/string_with_escapes.eterm
@@ -0,0 +1,5 @@
+[
+    <<"\n foo \/ bar \r\f\\", 239, 191, 186, "\t\b\"\\">>,
+    <<"\"and this string has an escape at the beginning">>,
+    <<"and this string has no escapes">>
+].

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_with_escapes.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_with_escapes.json b/src/jiffy/test/cases/string_with_escapes.json
new file mode 100644
index 0000000..532b5f4
--- /dev/null
+++ b/src/jiffy/test/cases/string_with_escapes.json
@@ -0,0 +1,3 @@
+["\n foo \/ bar \r\f\\\ufffa\t\b\"\\",
+ "\"and this string has an escape at the beginning",
+ "and this string has no escapes" ]

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_with_invalid_newline.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_with_invalid_newline.eterm b/src/jiffy/test/cases/string_with_invalid_newline.eterm
new file mode 100644
index 0000000..626a2f4
--- /dev/null
+++ b/src/jiffy/test/cases/string_with_invalid_newline.eterm
@@ -0,0 +1 @@
+{error,{67,invalid_string}}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/string_with_invalid_newline.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/string_with_invalid_newline.json b/src/jiffy/test/cases/string_with_invalid_newline.json
new file mode 100644
index 0000000..2756f26
--- /dev/null
+++ b/src/jiffy/test/cases/string_with_invalid_newline.json
@@ -0,0 +1,2 @@
+"la di dah.  this is a string, and I can do this, \n, but not this
+"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/three_byte_utf8.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/three_byte_utf8.eterm b/src/jiffy/test/cases/three_byte_utf8.eterm
new file mode 100644
index 0000000..111676b
--- /dev/null
+++ b/src/jiffy/test/cases/three_byte_utf8.eterm
@@ -0,0 +1,4 @@
+{[
+    {<<"matzue">>, <<230, 157, 190, 230, 177, 159>>},
+    {<<"asakusa">>, <<230, 181, 133, 232, 141, 137>>}
+]}.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/three_byte_utf8.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/three_byte_utf8.json b/src/jiffy/test/cases/three_byte_utf8.json
new file mode 100644
index 0000000..9c9e656
--- /dev/null
+++ b/src/jiffy/test/cases/three_byte_utf8.json
@@ -0,0 +1 @@
+{"matzue": "松江", "asakusa": "浅草"}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/true.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/true.eterm b/src/jiffy/test/cases/true.eterm
new file mode 100644
index 0000000..60c0d88
--- /dev/null
+++ b/src/jiffy/test/cases/true.eterm
@@ -0,0 +1 @@
+true.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/true.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/true.json b/src/jiffy/test/cases/true.json
new file mode 100644
index 0000000..27ba77d
--- /dev/null
+++ b/src/jiffy/test/cases/true.json
@@ -0,0 +1 @@
+true

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/true_then_garbage.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/true_then_garbage.eterm b/src/jiffy/test/cases/true_then_garbage.eterm
new file mode 100644
index 0000000..30b0113
--- /dev/null
+++ b/src/jiffy/test/cases/true_then_garbage.eterm
@@ -0,0 +1 @@
+{error,{5,invalid_trailing_data}}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/true_then_garbage.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/true_then_garbage.json b/src/jiffy/test/cases/true_then_garbage.json
new file mode 100644
index 0000000..9151612
--- /dev/null
+++ b/src/jiffy/test/cases/true_then_garbage.json
@@ -0,0 +1 @@
+truex
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/unescaped_bulgarian.eterm
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/unescaped_bulgarian.eterm b/src/jiffy/test/cases/unescaped_bulgarian.eterm
new file mode 100644
index 0000000..2a31b61
--- /dev/null
+++ b/src/jiffy/test/cases/unescaped_bulgarian.eterm
@@ -0,0 +1,5 @@
+[
+    <<208, 148, 208, 176, 32, 208, 156, 209, 131, 32, 208, 149, 208,
+        177, 208, 176, 32, 208, 156, 208, 176, 208, 185, 208, 186, 208,
+        176, 209, 130, 208, 176>>
+].

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/cases/unescaped_bulgarian.json
----------------------------------------------------------------------
diff --git a/src/jiffy/test/cases/unescaped_bulgarian.json b/src/jiffy/test/cases/unescaped_bulgarian.json
new file mode 100644
index 0000000..f9a70a6
--- /dev/null
+++ b/src/jiffy/test/cases/unescaped_bulgarian.json
@@ -0,0 +1 @@
+["Да Му Еба Майката"]

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/etap.erl
----------------------------------------------------------------------
diff --git a/src/jiffy/test/etap.erl b/src/jiffy/test/etap.erl
new file mode 100644
index 0000000..6924d09
--- /dev/null
+++ b/src/jiffy/test/etap.erl
@@ -0,0 +1,612 @@
+%% Copyright (c) 2008-2009 Nick Gerakines <nick@gerakines.net>
+%%
+%% Permission is hereby granted, free of charge, to any person
+%% obtaining a copy of this software and associated documentation
+%% files (the "Software"), to deal in the Software without
+%% restriction, including without limitation the rights to use,
+%% copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the
+%% Software is furnished to do so, subject to the following
+%% conditions:
+%%
+%% The above copyright notice and this permission notice shall be
+%% included in all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+%% OTHER DEALINGS IN THE SOFTWARE.
+%%
+%% @author Nick Gerakines <nick@gerakines.net> [http://socklabs.com/]
+%% @author Jeremy Wall <jeremy@marzhillstudios.com>
+%% @version 0.3.4
+%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines
+%% @reference http://testanything.org/wiki/index.php/Main_Page
+%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
+%% @todo Finish implementing the skip directive.
+%% @todo Document the messages handled by this receive loop.
+%% @todo Explain in documentation why we use a process to handle test input.
+%% @doc etap is a TAP testing module for Erlang components and applications.
+%% This module allows developers to test their software using the TAP method.
+%%
+%% <blockquote cite="http://en.wikipedia.org/wiki/Test_Anything_Protocol"><p>
+%% TAP, the Test Anything Protocol, is a simple text-based interface between
+%% testing modules in a test harness. TAP started life as part of the test
+%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl
+%% and probably others by the time you read this.
+%% </p></blockquote>
+%%
+%% The testing process begins by defining a plan using etap:plan/1, running
+%% a number of etap tests and then calling eta:end_tests/0. Please refer to
+%% the Erlang modules in the t directory of this project for example tests.
+-module(etap).
+-vsn("0.3.4").
+
+-export([
+    ensure_test_server/0,
+    start_etap_server/0,
+    test_server/1,
+    msg/1, msg/2,
+    diag/1, diag/2,
+    expectation_mismatch_message/3,
+    plan/1,
+    end_tests/0,
+    not_ok/2, ok/2, is_ok/2, is/3, isnt/3, any/3, none/3,
+    fun_is/3, expect_fun/3, expect_fun/4,
+    is_greater/3,
+    skip/1, skip/2,
+    datetime/1,
+    skip/3,
+    bail/0, bail/1,
+    test_state/0, failure_count/0
+]).
+
+-export([
+    contains_ok/3,
+    is_before/4
+]).
+
+-export([
+    is_pid/2,
+    is_alive/2,
+    is_mfa/3
+]).
+
+-export([
+    loaded_ok/2,
+    can_ok/2, can_ok/3,
+    has_attrib/2, is_attrib/3,
+    is_behaviour/2
+]).
+
+-export([
+    dies_ok/2,
+    lives_ok/2,
+    throws_ok/3
+]).
+
+
+-record(test_state, {
+    planned = 0,
+    count = 0,
+    pass = 0,
+    fail = 0,
+    skip = 0,
+    skip_reason = ""
+}).
+
+%% @spec plan(N) -> Result
+%%       N = unknown | skip | {skip, string()} | integer()
+%%       Result = ok
+%% @doc Create a test plan and boot strap the test server.
+plan(unknown) ->
+    ensure_test_server(),
+    etap_server ! {self(), plan, unknown},
+    ok;
+plan(skip) ->
+    io:format("1..0 # skip~n");
+plan({skip, Reason}) ->
+    io:format("1..0 # skip ~s~n", [Reason]);
+plan(N) when is_integer(N), N > 0 ->
+    ensure_test_server(),
+    etap_server ! {self(), plan, N},
+    ok.
+
+%% @spec end_tests() -> ok
+%% @doc End the current test plan and output test results.
+%% @todo This should probably be done in the test_server process.
+end_tests() ->
+    case whereis(etap_server) of
+        undefined -> self() ! true;
+        _ -> etap_server ! {self(), state}
+    end,
+    State = receive X -> X end,
+    if
+        State#test_state.planned == -1 ->
+            io:format("1..~p~n", [State#test_state.count]);
+        true ->
+            ok
+    end,
+    case whereis(etap_server) of
+        undefined -> ok;
+        _ -> etap_server ! done, ok
+    end.
+
+bail() ->
+    bail("").
+
+bail(Reason) ->
+    etap_server ! {self(), diag, "Bail out! " ++ Reason},
+    etap_server ! done, ok,
+    ok.
+
+%% @spec test_state() -> Return
+%%       Return = test_state_record() | {error, string()}
+%% @doc Return the current test state
+test_state() ->
+    etap_server ! {self(), state},
+    receive
+	X when is_record(X, test_state) -> X
+    after
+	1000 -> {error, "Timed out waiting for etap server reply.~n"}
+    end.
+
+%% @spec failure_count() -> Return
+%%       Return = integer() | {error, string()}
+%% @doc Return the current failure count
+failure_count() ->
+    case test_state() of
+        #test_state{fail=FailureCount} -> FailureCount;
+        X -> X
+    end.
+
+%% @spec msg(S) -> ok
+%%       S = string()
+%% @doc Print a message in the test output.
+msg(S) -> etap_server ! {self(), diag, S}, ok.
+
+%% @spec msg(Format, Data) -> ok
+%%      Format = atom() | string() | binary()
+%%      Data = [term()]
+%%      UnicodeList = [Unicode]
+%%      Unicode = int()
+%% @doc Print a message in the test output.
+%% Function arguments are passed through io_lib:format/2.
+msg(Format, Data) -> msg(io_lib:format(Format, Data)).
+
+%% @spec diag(S) -> ok
+%%       S = string()
+%% @doc Print a debug/status message related to the test suite.
+diag(S) -> msg("# " ++ S).
+
+%% @spec diag(Format, Data) -> ok
+%%      Format = atom() | string() | binary()
+%%      Data = [term()]
+%%      UnicodeList = [Unicode]
+%%      Unicode = int()
+%% @doc Print a debug/status message related to the test suite.
+%% Function arguments are passed through io_lib:format/2.
+diag(Format, Data) -> diag(io_lib:format(Format, Data)).
+
+%% @spec expectation_mismatch_message(Got, Expected, Desc) -> ok
+%%       Got = any()
+%%       Expected = any()
+%%       Desc = string()
+%% @doc Print an expectation mismatch message in the test output.
+expectation_mismatch_message(Got, Expected, Desc) ->
+    msg("    ---"),
+    msg("    description: ~p", [Desc]),
+    msg("    found:       ~p", [Got]),
+    msg("    wanted:      ~p", [Expected]),
+    msg("    ..."),
+    ok.
+
+% @spec evaluate(Pass, Got, Expected, Desc) -> Result
+%%       Pass = true | false
+%%       Got = any()
+%%       Expected = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Evaluate a test statement, printing an expectation mismatch message
+%%       if the test failed.
+evaluate(Pass, Got, Expected, Desc) ->
+    case mk_tap(Pass, Desc) of
+        false ->
+            expectation_mismatch_message(Got, Expected, Desc),
+            false;
+        true ->
+            true
+    end.
+
+%% @spec ok(Expr, Desc) -> Result
+%%       Expr = true | false
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that a statement is true.
+ok(Expr, Desc) -> evaluate(Expr == true, Expr, true, Desc).
+
+%% @spec not_ok(Expr, Desc) -> Result
+%%       Expr = true | false
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that a statement is false.
+not_ok(Expr, Desc) -> evaluate(Expr == false, Expr, false, Desc).
+
+%% @spec is_ok(Expr, Desc) -> Result
+%%       Expr = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that two values are the same.
+is_ok(Expr, Desc) -> evaluate(Expr == ok, Expr, ok, Desc).
+
+%% @spec is(Got, Expected, Desc) -> Result
+%%       Got = any()
+%%       Expected = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that two values are the same.
+is(Got, Expected, Desc) -> evaluate(Got == Expected, Got, Expected, Desc).
+
+%% @spec isnt(Got, Expected, Desc) -> Result
+%%       Got = any()
+%%       Expected = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that two values are not the same.
+isnt(Got, Expected, Desc) -> evaluate(Got /= Expected, Got, Expected, Desc).
+
+%% @spec is_greater(ValueA, ValueB, Desc) -> Result
+%%       ValueA = number()
+%%       ValueB = number()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that an integer is greater than another.
+is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) ->
+    mk_tap(ValueA > ValueB, Desc).
+
+%% @spec any(Got, Items, Desc) -> Result
+%%       Got = any()
+%%       Items = [any()]
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that an item is in a list.
+any(Got, Items, Desc) when is_function(Got) ->
+    is(lists:any(Got, Items), true, Desc);
+any(Got, Items, Desc) ->
+    is(lists:member(Got, Items), true, Desc).
+
+%% @spec none(Got, Items, Desc) -> Result
+%%       Got = any()
+%%       Items = [any()]
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Assert that an item is not in a list.
+none(Got, Items, Desc) when is_function(Got) ->
+    is(lists:any(Got, Items), false, Desc);
+none(Got, Items, Desc) ->
+    is(lists:member(Got, Items), false, Desc).
+
+%% @spec fun_is(Fun, Expected, Desc) -> Result
+%%       Fun = function()
+%%       Expected = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Use an anonymous function to assert a pattern match.
+fun_is(Fun, Expected, Desc) when is_function(Fun) ->
+    is(Fun(Expected), true, Desc).
+
+%% @spec expect_fun(ExpectFun, Got, Desc) -> Result
+%%       ExpectFun = function()
+%%       Got = any()
+%%       Desc = string()
+%%       Result = true | false
+%% @doc Use an anonymous function to assert a pattern match, using actual
+%%       value as the argument to the function.
+expect_fun(ExpectFun, Got, Desc) ->
+    evaluate(ExpectFun(Got), Got, ExpectFun, Desc).
+
+%% @spec expect_fun(ExpectFun, Got, Desc, ExpectStr) -> Result
+%%       ExpectFun = function()
+%%       Got = any()
+%%       Desc = string()
+%%       ExpectStr = string()
+%%       Result = true | false
+%% @doc Use an anonymous function to assert a pattern match, using actual
+%%       value as the argument to the function.
+expect_fun(ExpectFun, Got, Desc, ExpectStr) ->
+    evaluate(ExpectFun(Got), Got, ExpectStr, Desc).
+
+%% @equiv skip(TestFun, "")
+skip(TestFun) when is_function(TestFun) ->
+    skip(TestFun, "").
+
+%% @spec skip(TestFun, Reason) -> ok
+%%       TestFun = function()
+%%       Reason = string()
+%% @doc Skip a test.
+skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
+    begin_skip(Reason),
+    catch TestFun(),
+    end_skip(),
+    ok.
+
+%% @spec skip(Q, TestFun, Reason) -> ok
+%%       Q = true | false | function()
+%%       TestFun = function()
+%%       Reason = string()
+%% @doc Skips a test conditionally. The first argument to this function can
+%% either be the 'true' or 'false' atoms or a function that returns 'true' or
+%% 'false'.
+skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason)
->
+    case QFun() of
+        true -> begin_skip(Reason), TestFun(), end_skip();
+        _ -> TestFun()
+    end,
+    ok;
+
+skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true ->
+    begin_skip(Reason),
+    TestFun(),
+    end_skip(),
+    ok;
+
+skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
+    TestFun(),
+    ok.
+
+%% @private
+begin_skip(Reason) ->
+    etap_server ! {self(), begin_skip, Reason}.
+
+%% @private
+end_skip() ->
+    etap_server ! {self(), end_skip}.
+
+%% @spec contains_ok(string(), string(), string()) -> true | false
+%% @doc Assert that a string is contained in another string.
+contains_ok(Source, String, Desc) ->
+    etap:isnt(
+        string:str(Source, String),
+        0,
+        Desc
+    ).
+
+%% @spec is_before(string(), string(), string(), string()) -> true | false
+%% @doc Assert that a string comes before another string within a larger body.
+is_before(Source, StringA, StringB, Desc) ->
+    etap:is_greater(
+        string:str(Source, StringB),
+        string:str(Source, StringA),
+        Desc
+    ).
+
+%% @doc Assert that a given variable is a pid.
+is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc);
+is_pid(_, Desc) -> etap:ok(false, Desc).
+
+%% @doc Assert that a given process/pid is alive.
+is_alive(Pid, Desc) ->
+    etap:ok(erlang:is_process_alive(Pid), Desc).
+
+%% @doc Assert that the current function of a pid is a given {M, F, A} tuple.
+is_mfa(Pid, MFA, Desc) ->
+    etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc).
+
+%% @spec loaded_ok(atom(), string()) -> true | false
+%% @doc Assert that a module has been loaded successfully.
+loaded_ok(M, Desc) when is_atom(M) ->
+    etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc).
+
+%% @spec can_ok(atom(), atom()) -> true | false
+%% @doc Assert that a module exports a given function.
+can_ok(M, F) when is_atom(M), is_atom(F) ->
+    Matches = [X || {X, _} <- M:module_info(exports), X == F],
+    etap:ok(Matches > 0, lists:concat([M, " can ", F])).
+
+%% @spec can_ok(atom(), atom(), integer()) -> true | false
+%% @doc Assert that a module exports a given function with a given arity.
+can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) ->
+    Matches = [X || X <- M:module_info(exports), X == {F, A}],
+    etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])).
+
+%% @spec has_attrib(M, A) -> true | false
+%%       M = atom()
+%%       A = atom()
+%% @doc Asserts that a module has a given attribute.
+has_attrib(M, A) when is_atom(M), is_atom(A) ->
+    etap:isnt(
+        proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'),
+        'asdlkjasdlkads',
+        lists:concat([M, " has attribute ", A])
+    ).
+
+%% @spec has_attrib(M, A. V) -> true | false
+%%       M = atom()
+%%       A = atom()
+%%       V = any()
+%% @doc Asserts that a module has a given attribute with a given value.
+is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) ->
+    etap:is(
+        proplists:get_value(A, M:module_info(attributes)),
+        [V],
+        lists:concat([M, "'s ", A, " is ", V])
+    ).
+
+%% @spec is_behavior(M, B) -> true | false
+%%       M = atom()
+%%       B = atom()
+%% @doc Asserts that a given module has a specific behavior.
+is_behaviour(M, B) when is_atom(M) andalso is_atom(B) ->
+    is_attrib(M, behaviour, B).
+
+%% @doc Assert that an exception is raised when running a given function.
+dies_ok(F, Desc) ->
+    case (catch F()) of
+        {'EXIT', _} -> etap:ok(true, Desc);
+        _ -> etap:ok(false, Desc)
+    end.
+
+%% @doc Assert that an exception is not raised when running a given function.
+lives_ok(F, Desc) ->
+    etap:is(try_this(F), success, Desc).
+
+%% @doc Assert that the exception thrown by a function matches the given exception.
+throws_ok(F, Exception, Desc) ->
+    try F() of
+        _ -> etap:ok(nok, Desc)
+    catch
+        _:E ->
+            etap:is(E, Exception, Desc)
+    end.
+
+%% @private
+%% @doc Run a function and catch any exceptions.
+try_this(F) when is_function(F, 0) ->
+    try F() of
+        _ -> success
+    catch
+        throw:E -> {throw, E};
+        error:E -> {error, E};
+        exit:E -> {exit, E}
+    end.
+
+%% @private
+%% @doc Start the etap_server process if it is not running already.
+ensure_test_server() ->
+    case whereis(etap_server) of
+        undefined ->
+            proc_lib:start(?MODULE, start_etap_server,[]);
+        _ ->
+            diag("The test server is already running.")
+    end.
+
+%% @private
+%% @doc Start the etap_server loop and register itself as the etap_server
+%% process.
+start_etap_server() ->
+    catch register(etap_server, self()),
+    proc_lib:init_ack(ok),
+    etap:test_server(#test_state{
+        planned = 0,
+        count = 0,
+        pass = 0,
+        fail = 0,
+        skip = 0,
+        skip_reason = ""
+    }).
+
+
+%% @private
+%% @doc The main etap_server receive/run loop. The etap_server receive loop
+%% responds to seven messages apperatining to failure or passing of tests.
+%% It is also used to initiate the testing process with the {_, plan, _}
+%% message that clears the current test state.
+test_server(State) ->
+    NewState = receive
+        {_From, plan, unknown} ->
+            io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
+            io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes,
etap:module_info())) ]),
+            State#test_state{
+                planned = -1,
+                count = 0,
+                pass = 0,
+                fail = 0,
+                skip = 0,
+                skip_reason = ""
+            };
+        {_From, plan, N} ->
+            io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
+            io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes,
etap:module_info())) ]),
+            io:format("1..~p~n", [N]),
+            State#test_state{
+                planned = N,
+                count = 0,
+                pass = 0,
+                fail = 0,
+                skip = 0,
+                skip_reason = ""
+            };
+        {_From, begin_skip, Reason} ->
+            State#test_state{
+                skip = 1,
+                skip_reason = Reason
+            };
+        {_From, end_skip} ->
+            State#test_state{
+                skip = 0,
+                skip_reason = ""
+            };
+        {_From, pass, Desc} ->
+            FullMessage = skip_diag(
+                " - " ++ Desc,
+                State#test_state.skip,
+                State#test_state.skip_reason
+            ),
+            io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
+            State#test_state{
+                count = State#test_state.count + 1,
+                pass = State#test_state.pass + 1
+            };
+
+        {_From, fail, Desc} ->
+            FullMessage = skip_diag(
+                " - " ++ Desc,
+                State#test_state.skip,
+                State#test_state.skip_reason
+            ),
+            io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
+            State#test_state{
+                count = State#test_state.count + 1,
+                fail = State#test_state.fail + 1
+            };
+        {From, state} ->
+            From ! State,
+            State;
+        {_From, diag, Message} ->
+            io:format("~s~n", [Message]),
+            State;
+        {From, count} ->
+            From ! State#test_state.count,
+            State;
+        {From, is_skip} ->
+            From ! State#test_state.skip,
+            State;
+        done ->
+            exit(normal)
+    end,
+    test_server(NewState).
+
+%% @private
+%% @doc Process the result of a test and send it to the etap_server process.
+mk_tap(Result, Desc) ->
+    IsSkip = lib:sendw(etap_server, is_skip),
+    case [IsSkip, Result] of
+        [_, true] ->
+            etap_server ! {self(), pass, Desc},
+            true;
+        [1, _] ->
+            etap_server ! {self(), pass, Desc},
+            true;
+        _ ->
+            etap_server ! {self(), fail, Desc},
+            false
+    end.
+
+%% @private
+%% @doc Format a date/time string.
+datetime(DateTime) ->
+    {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
+    io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month,
Day, Hour, Min, Sec]).
+
+%% @private
+%% @doc Craft an output message taking skip/todo into consideration.
+skip_diag(Message, 0, _) ->
+    Message;
+skip_diag(_Message, 1, "") ->
+    " # SKIP";
+skip_diag(_Message, 1, Reason) ->
+    " # SKIP : " ++ Reason.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/jiffy_tests.erl
----------------------------------------------------------------------
diff --git a/src/jiffy/test/jiffy_tests.erl b/src/jiffy/test/jiffy_tests.erl
new file mode 100644
index 0000000..0655b0d
--- /dev/null
+++ b/src/jiffy/test/jiffy_tests.erl
@@ -0,0 +1,135 @@
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+-module(jiffy_tests).
+
+-ifdef(JIFFY_DEV).
+
+-include_lib("proper/include/proper.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+proper_test_() ->
+    PropErOpts = [
+        {to_file, user},
+        {max_size, 15},
+        {numtests, 1000}
+    ],
+    {timeout, 3600, ?_assertEqual([], proper:module(jiffy_tests, PropErOpts))}.
+
+
+prop_encode_decode() ->
+    ?FORALL(Data, json(),
+        begin
+            %io:format(standard_error, "Data: ~p~n", [Data]),
+            Data == jiffy:decode(jiffy:encode(Data))
+        end
+    ).
+
+prop_encode_decode_pretty() ->
+    ?FORALL(Data, json(),
+        begin
+            Data == jiffy:decode(jiffy:encode(Data, [pretty]))
+        end
+    ).
+
+prop_encode_not_crash() ->
+    ?FORALL(Data, any(), begin catch jiffy:encode(Data), true end).
+
+prop_decode_not_crash_bin() ->
+    ?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end).
+
+prop_decode_not_crash_any() ->
+    ?FORALL(Data, any(), begin catch jiffy:decode(Data), true end).
+
+
+% JSON Generation
+
+
+json_null() ->
+    null.
+
+
+json_boolean() ->
+    oneof([true, false]).
+
+
+json_number() ->
+    oneof([integer(), float()]).
+
+
+json_string() ->
+    escaped_utf8_bin().
+
+
+json_list(S) when S =< 0 ->
+    [];
+json_list(S) ->
+    ?LETSHRINK(
+        [ListSize],
+        [integer(0, S)],
+        vector(ListSize, json_text(S - ListSize))
+    ).
+
+
+json_object(S) when S =< 0 ->
+    {[]};
+json_object(S) ->
+    ?LETSHRINK(
+        [ObjectSize],
+        [integer(0, S)],
+        {vector(ObjectSize, {json_string(), json_text(S - ObjectSize)})}
+    ).
+
+
+json_value() ->
+    oneof([
+        json_null(),
+        json_boolean(),
+        json_string(),
+        json_number()
+    ]).
+
+
+json_text(S) when S > 0 ->
+    ?LAZY(oneof([
+        json_list(S),
+        json_object(S)
+    ]));
+json_text(_) ->
+    json_value().
+
+
+json() ->
+    ?SIZED(S, json_text(S)).
+
+
+%% XXX: Add generators
+%
+% We should add generators that generate JSON binaries directly
+% so we can test things that aren't produced by the encoder.
+%
+% We should also have a version of the JSON generator that inserts
+% errors into the JSON that we can test for.
+
+
+escaped_utf8_bin() ->
+    ?SUCHTHAT(Bin,
+        ?LET(S, ?SUCHTHAT(L, list(escaped_char()), L /= []),
+        unicode:characters_to_binary(S, unicode, utf8)),
+        is_binary(Bin)
+    ).
+
+
+escaped_char() ->
+    ?LET(C, char(),
+        case C of
+            $" -> "\\\"";
+            C when C == 65534 -> 65533;
+            C when C == 65535 -> 65533;
+            C when C > 1114111 -> 1114111;
+            C -> C
+        end
+    ).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/util.erl
----------------------------------------------------------------------
diff --git a/src/jiffy/test/util.erl b/src/jiffy/test/util.erl
new file mode 100644
index 0000000..ceaef89
--- /dev/null
+++ b/src/jiffy/test/util.erl
@@ -0,0 +1,44 @@
+-module(util).
+-export([test_good/1, test_good/2, test_errors/1]).
+
+test_good(Cases) ->
+    test_good(Cases, []).
+
+test_good(Cases, Options) ->
+    lists:foreach(fun(Case) -> check_good(Case, Options) end, Cases).
+
+test_errors(Cases) ->
+    lists:foreach(fun(Case) -> check_error(Case) end, Cases).
+
+ok_dec(J, _E) ->
+    lists:flatten(io_lib:format("Decoded ~p.", [J])).
+
+ok_enc(E, _J) ->
+    lists:flatten(io_lib:format("Encoded ~p", [E])).
+
+do_encode(E, Options) ->
+    iolist_to_binary(jiffy:encode(E, Options)).
+
+error_mesg(J) ->
+    lists:flatten(io_lib:format("Decoding ~p returns an error.", [J])).
+
+check_good({J, E}, Options) ->
+    etap:is(jiffy:decode(J), E, ok_dec(J, E)),
+    etap:is(do_encode(E, Options), J, ok_enc(E, J));
+check_good({J, E, J2}, Options) ->
+    etap:is(jiffy:decode(J), E, ok_dec(J, E)),
+    etap:is(do_encode(E, Options), J2, ok_enc(E, J2)).
+
+check_error({J, E}) ->
+    etap:fun_is(
+        fun({error, E1}) when E1 == E -> true; (E1) -> E1 end,
+        (catch jiffy:decode(J)),
+        error_mesg(J)
+    );
+check_error(J) ->
+    etap:fun_is(
+        fun({error, _}) -> true; (Else) -> Else end,
+        (catch jiffy:decode(J)),
+        error_mesg(J)
+    ).
+


Mime
View raw message