Fix JS tests on SpiderMonkey 60

It turns out the infinite loop can now run fast enough that we exhaust
the stack space in a given couchjs process. This ends up leaving a
couchjs process that can't be used for other things as the `map_results`
buffer doesn't get reset inbetween calls.
diff --git a/share/server/state.js b/share/server/state.js
index ff553dd..76fb68b 100644
--- a/share/server/state.js
+++ b/share/server/state.js
@@ -16,6 +16,7 @@
     State.funs = [];
     State.lib = null;
     State.query_config = config || {};
+    Views.reset();
     gc();
     print("true"); // indicates success
   },
diff --git a/share/server/views.js b/share/server/views.js
index 7c9953d..623f47d 100644
--- a/share/server/views.js
+++ b/share/server/views.js
@@ -56,6 +56,7 @@
   };
 
   function handleViewError(err, doc) {
+    map_results = [];
     if (err == "fatal_error") {
       // Only if it's a "fatal_error" do we exit. What's a fatal error?
       // That's for the query to decide.
@@ -82,6 +83,9 @@
 
   return {
     // view helper functions
+    reset : function() {
+        map_results = [];
+    },
     emit : function(key, value) {
       map_results.push([key, value]);
     },
diff --git a/src/couch/test/eunit/couch_js_tests.erl b/src/couch/test/eunit/couch_js_tests.erl
new file mode 100644
index 0000000..540d8b1
--- /dev/null
+++ b/src/couch/test/eunit/couch_js_tests.erl
@@ -0,0 +1,47 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_js_tests).
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(FUNC, <<
+  "function(doc) {\n"
+  "  var val = \"0123456789ABCDEF\";\n"
+  "  while(true) {emit(val, val);}\n"
+  "}\n"
+>>).
+
+
+couch_js_test_() ->
+    {
+        "Test couchjs",
+        {
+            setup,
+            fun test_util:start_couch/0,
+            fun test_util:stop_couch/1,
+            [
+                fun should_recover_from_oom/0
+            ]
+        }
+    }.
+
+
+should_recover_from_oom() ->
+    Proc = couch_query_servers:get_os_process(<<"javascript">>),
+    R1 = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, ?FUNC]),
+    R2 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, <<"{}">>]),
+    R3 = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, ?FUNC]),
+
+    ?assertEqual(true, R1),
+    ?assertEqual([[]], R2),
+    ?assertEqual(true, R3).
diff --git a/test/javascript/tests/view_errors.js b/test/javascript/tests/view_errors.js
index 7577b80..43db3c8 100644
--- a/test/javascript/tests/view_errors.js
+++ b/test/javascript/tests/view_errors.js
@@ -145,7 +145,7 @@
         _id:"_design/infinite",
         language: "javascript",
         views: {
-          "infinite_loop" :{map:"function(doc) {while(true){emit(doc,doc);}};"}
+          "infinite_loop" :{map:"function(doc) {while(true){};}"}
         }
       };
       T(db.save(designDoc3).ok);
@@ -154,7 +154,7 @@
           db.view("infinite/infinite_loop");
           T(0 == 1);
       } catch(e) {
-          T(e.error == "os_process_error" || e.error == "unnamed_error");
+          T(e.error == "os_process_error");
       }
 
       // Check error responses for invalid multi-get bodies.