diff --git a/Dockerfile b/Dockerfile index f76a4a24..79e2e6b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,24 +5,26 @@ # Pull base image. FROM dockerfile/ubuntu + +# Set the env variables to non-interactive +ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_PRIORITY critical +ENV DEBCONF_NOWARNINGS yes +ENV TERM linux +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +# ADD codewarrior user +RUN useradd codewarrior +RUN rm -rf ~codewarrior && cp -a ~root ~codewarrior && chown -R codewarrior:codewarrior ~codewarrior RUN apt-get install -y python python-dev python-pip python-virtualenv # Define mountable directories. # Install Node.js -RUN \ - cd /tmp && \ - wget http://nodejs.org/dist/node-latest.tar.gz && \ - tar xvzf node-latest.tar.gz && \ - rm -f node-latest.tar.gz && \ - cd node-v* && \ - ./configure && \ - CXX="g++ -Wno-unused-local-typedefs" make && \ - CXX="g++ -Wno-unused-local-typedefs" make install && \ - cd /tmp && \ - rm -rf /tmp/node-v* && \ - echo '\n# Node.js\nexport PATH="node_modules/.bin:$PATH"' >> /root/.bash_profile - +RUN add-apt-repository ppa:chris-lea/node.js +RUN apt-get update +RUN apt-get install -y nodejs +RUN su codewarrior -c "echo '\n# Node.js\nexport PATH=\"/codewars/node_modules/.bin:$PATH\"' >> ~codewarrior/.bash_profile" # Define default command. CMD ["bash"] @@ -40,12 +42,8 @@ RUN apt-get install -y fsharp # Install Coffeescript RUN npm -g install coffee-script -# Install Node testing frameworks -RUN npm -g install chai -RUN npm -g install mocha - -# Install additional node frameworks -RUN npm install immutable +# Install Node testing frameworks & additional frameworks +RUN npm -g install chai mocha immutable # Install Lua RUN apt-get install -y lua5.2 @@ -64,18 +62,14 @@ RUN echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-s echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections RUN apt-get install -y oracle-java8-installer -# Install Clojure +# Install Clojure (well, install Leiningen) RUN curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > /usr/bin/lein RUN chmod a+x /usr/bin/lein -# Add a few packages by default -RUN mkdir ~/.lein && echo '{:user {:dependencies [[org.clojure/clojure "1.6.0"] [junit/junit "4.11"] [org.hamcrest/hamcrest-core "1.3"]]}}' > ~/.lein/profiles.clj -RUN echo '(defproject codewars "Docker")' > project.clj -RUN LEIN_ROOT=true lein deps # Install Haskell -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y ghc cabal-install -RUN cabal update -RUN cabal install hspec +RUN apt-get install -y ghc cabal-install +RUN su codewarrior -c "cabal update" +RUN su codewarrior -c "cd ~codewarrior ; cabal install hspec" # Install Julia # Julia is really slow, but v0.3 is okay (see http://stackoverflow.com/a/20566032) @@ -90,7 +84,10 @@ RUN printf '#!/bin/bash\njulia-noisy "$@" 2> >(grep -v "OpenBLAS : Your OS does RUN chmod a+x /usr/bin/julia # Install erlang -RUN apt-get -y install erlang +RUN echo "deb http://packages.erlang-solutions.com/ubuntu trusty contrib" >> /etc/apt/sources.list +RUN curl http://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | apt-key add - +RUN apt-get update +RUN apt-get -y install erlang-nox erlang-dev # Install PHP RUN apt-get -y install php5-cli @@ -98,16 +95,15 @@ RUN apt-get -y install php5-cli # Install GoLang WORKDIR /tmp # http://blog.labix.org/2013/06/15/in-flight-deb-packages-of-go -RUN curl https://godeb.s3.amazonaws.com/godeb-amd64.tar.gz | tar zxv -RUN ./godeb install 1.3 -RUN rm godeb +# This was cool but then it stopped working... that sucks... ~Matt +#RUN curl https://godeb.s3.amazonaws.com/godeb-amd64.tar.gz | tar zxv +#RUN ./godeb install 1.3.1 +#RUN rm godeb +RUN apt-get install -y golang # Install TypeScript RUN npm -g install typescript -# Install Pip -RUN apt-get install python-pip - #Install ruby RUN apt-get install -y python-software-properties && \ apt-add-repository -y ppa:brightbox/ruby-ng && \ @@ -135,23 +131,21 @@ RUN gem install rspec-its --no-ri --no-rdoc #RUN gem install minitest --no-ri --no-rdoc # Install additional gems - RUN gem install rails --no-ri --no-rdoc # Install SQLITE RUN apt-get install -y sqlite libsqlite3-dev RUN gem install sqlite3 --no-ri --no-rdoc -RUN npm install sqlite3 +RUN npm -g install sqlite3 # Install MongoDB RUN apt-get install -y mongodb-server && \ - mkdir /data && \ - mkdir /data/db + mkdir -p /data/db && \ + chown codewarrior:codewarrior /data/db # Install mongo packages for languages -RUN npm install mongoose -RUN npm install mongodb +RUN npm -g install mongoose mongodb RUN pip install pymongo RUN gem install mongo --no-ri --no-rdoc RUN gem install mongoid --no-ri --no-rdoc @@ -160,7 +154,7 @@ RUN gem install mongoid --no-ri --no-rdoc RUN apt-get install -y redis-server # Install Redis Language packages -RUN npm install redis +RUN npm -g install redis RUN gem install redis --no-ri --no-rdoc RUN pip install redis @@ -179,8 +173,21 @@ RUN apt-get -y install clang-3.4 lldb-3.4 # ADD cli-runner and install node deps ADD . /codewars + +# Build the jvm-runner +WORKDIR /codewars/jvm-runner +RUN [ -e target/jvm-runner-0.1.1-standalone.jar ] || LEIN_ROOT=true lein do clean, test, uberjar + WORKDIR /codewars RUN npm install + +# Run the test suite to make sure this thing works + +USER codewarrior +# Set environment variables +ENV TIMEOUT 2000 +ENV USER codewarrior +ENV HOME /home/codewarrior RUN mocha -t 5000 test/* #timeout is a fallback in case an error with node diff --git a/frameworks/clojure/clojure/test/codewars.clj b/frameworks/clojure/clojure/test/codewars.clj deleted file mode 100644 index 62c15baa..00000000 --- a/frameworks/clojure/clojure/test/codewars.clj +++ /dev/null @@ -1,79 +0,0 @@ -(ns clojure.test.codewars - (:refer-clojure :exclude [time]) - (:require - [clojure.test :refer :all :exclude [run-tests with-test-out]] - [clojure.string] - [clojure.stacktrace :as stack])) - -(defmacro with-test-out [& body] - `(-> - (with-out-str ~@body) - (clojure.string/replace "\n" "<:LF:>\n") - (print) - clojure.test/with-test-out)) - -(defn- print-context [] - (when (seq *testing-contexts*) - (->> (testing-contexts-str) (str "") println))) - -(defn- print-with-message [status {:keys [:message]}] - (if (string? message) - (println (str status message)) - (println status))) - -(defn- expr-str [expression] - (if (instance? Throwable expression) - (with-out-str - (stack/print-cause-trace expression *stack-trace-depth*)) - (pr-str expression))) - -(defn- print-expectations [{:keys [:expected :actual]}] - (println "expected:" (pr-str expected) "- actual:" (expr-str actual))) - -(defmulti codewars-report :type) - -(defmethod codewars-report :pass [_] - (with-test-out - (print-context) - (println "Test Passed"))) - -(defmethod codewars-report :fail [m] - (with-test-out - (print-context) - (print-with-message "" m) - (print-expectations m)) - (flush) - (System/exit 1)) - -(defmethod codewars-report :error [m] - (with-test-out - (print-context) - (print-with-message "" m) - (print-expectations m)) - (flush) - (System/exit 1)) - -(defmethod codewars-report :begin-test-ns [_]) -(defmethod codewars-report :end-test-ns [_]) - -(defmethod codewars-report :begin-test-var [m] - (with-test-out - (print "") - (-> m :var (. sym) println))) -(defmethod codewars-report :end-test-var [_]) - -(defmethod codewars-report :summary [_]) - -(defmacro time - [expr] - `(let [start# (System/nanoTime) - ret# ~expr] - (println (str "" - (/ (double (- (System/nanoTime) start#)) 1000000.0) - " msecs <:LF:>")) - ret#)) - -(defmacro run-tests [] - (binding [clojure.test/report - clojure.test.codewars/codewars-report] - (time (clojure.test/run-tests)))) diff --git a/frameworks/java/CwRunListener.java b/frameworks/java/CwRunListener.java deleted file mode 100644 index 0598bda1..00000000 --- a/frameworks/java/CwRunListener.java +++ /dev/null @@ -1,29 +0,0 @@ -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.junit.runner.Description; - -public class CwRunListener extends RunListener -{ - private boolean failed; - public void testFailure(Failure failure) - { - failed = true; - System.out.println("" + formatMessage(failure.getMessage())); - } - public void testStarted(Description description) - { - System.out.println("" + formatMessage(description.getDisplayName())); - failed = false; - } - public void testFinished(Description description) - { - if(!failed) - { - System.out.println("Test Passed"); - } - } - private static String formatMessage(String s) - { - return s.replaceAll("\n", "<:LF:>"); - } -} diff --git a/frameworks/java/CwTestRunner.java b/frameworks/java/CwTestRunner.java deleted file mode 100644 index bb62c379..00000000 --- a/frameworks/java/CwTestRunner.java +++ /dev/null @@ -1,11 +0,0 @@ -import org.junit.runner.JUnitCore; - -public class CwTestRunner -{ - public static void main(String[] args) - { - JUnitCore core = new JUnitCore(); - core.addListener(new CwRunListener()); - core.run(TestFixture.class); - } -} diff --git a/jvm-runner/Dockerfile b/jvm-runner/Dockerfile index 61ac3178..e53a54d0 100644 --- a/jvm-runner/Dockerfile +++ b/jvm-runner/Dockerfile @@ -19,18 +19,19 @@ RUN chmod a+x /usr/bin/lein RUN LEIN_ROOT=true lein --version # ADD codewarrior user -RUN useradd -s /usr/sbin/nologin codewarrior +RUN useradd -s /usr/sbin/nologin -m -d /home/codewarrior -p codewarrior codewarrior # ADD cli-runner and install node deps ADD . /codewars-jvm WORKDIR /codewars-jvm -RUN LEIN_ROOT=true lein do clean, test, uberjar +RUN [ -e target/jvm-runner-0.1.1-standalone.jar ] || LEIN_ROOT=true lein do clean, test, uberjar # Set the user and workdir WORKDIR /tmp USER codewarrior # Run the uberjar -# TODO: Infer the version number for this somehow ENV TIMEOUT 2000 +ENV USER codewarrior +ENV HOME /home/codewarrior ENTRYPOINT ["java", "-jar", "/codewars-jvm/target/jvm-runner-0.1.1-standalone.jar"] diff --git a/jvm-runner/project.clj b/jvm-runner/project.clj index b8d8ede7..cd6966b9 100644 --- a/jvm-runner/project.clj +++ b/jvm-runner/project.clj @@ -4,8 +4,8 @@ :javac-target "1.8" :dependencies [[org.clojure/clojure "1.6.0"] [cheshire "5.3.1"] - [org.tcrawley/dynapath "0.2.3"] [junit/junit "4.11"] + [org.codehaus.groovy/groovy-all "2.3.6"] [environ "0.5.0"]] :plugins [[lein-environ "0.5.0"]] :source-paths ["src/clojure"] diff --git a/jvm-runner/src/clojure/codewars/core.clj b/jvm-runner/src/clojure/codewars/core.clj index 8481a311..fbfec5ac 100644 --- a/jvm-runner/src/clojure/codewars/core.clj +++ b/jvm-runner/src/clojure/codewars/core.clj @@ -1,10 +1,11 @@ (ns codewars.core (:require [cheshire.core :as json] - [codewars.runner :refer [run]] + [codewars.runners :refer [run]] [codewars.kill-switch :refer [with-timeout]] [environ.core :refer [env]] - [codewars.runner.java] - [codewars.runner.clojure]) + [codewars.runners.groovy] + [codewars.runners.clojure] + [codewars.runners.java]) (:import [java.util.concurrent TimeoutException]) (:gen-class)) @@ -15,12 +16,15 @@ (defn- fail [e] (binding [*out* *err*] (println (str "" (.getMessage e) "<:LF:>"))) - (System/exit 1)) + ;(System/exit 1) + ) (defn -main "Listens to *in* for a JSON message, parses it and calls the appropriate runner" [& _] - (let [ms (-> env :timeout Integer/parseInt) + (let [ms ((fnil #(Integer/parseInt %) + "5000") + (env :timeout)) input (json/parse-stream *in* true)] (try (flush-out (with-timeout ms (run input))) diff --git a/jvm-runner/src/clojure/codewars/runner/clojure.clj b/jvm-runner/src/clojure/codewars/runner/clojure.clj deleted file mode 100644 index ddb6d050..00000000 --- a/jvm-runner/src/clojure/codewars/runner/clojure.clj +++ /dev/null @@ -1,49 +0,0 @@ -(ns codewars.runner.clojure - (:require [codewars.runner :refer [solution-only full-project]] - [codewars.util :as util] - [codewars.clojure.test] - [clojure.java.io :as io] - [dynapath.util :as dp]) - (:import [codewars.java TempDir]) - (:refer-clojure :exclude (add-classpath))) - -;; Extracted from cemerick.pomegranate -(defn- classloader-hierarchy - "Returns a seq of classloaders, with the tip of the hierarchy first. - Uses the current thread context ClassLoader as the tip ClassLoader if one is not provided." - ([] (classloader-hierarchy (.. Thread currentThread getContextClassLoader))) - ([tip] - (->> tip - (iterate #(.getParent %)) - (take-while boolean)))) - -(defn- add-classpath - "A fixed version of the (deprecated) `add-classpath` in clojure.core." - ([jar-or-dir classloader] - (if-not (dp/add-classpath-url classloader (.toURL (.toURI (io/file jar-or-dir)))) - (throw (IllegalStateException. (str classloader " is not a modifiable classloader"))))) - ([jar-or-dir] - (let [classloaders (classloader-hierarchy)] - (if-let [cl (last (filter dp/addable-classpath? classloaders))] - (add-classpath jar-or-dir cl) - (throw (IllegalStateException. (str "Could not find a suitable classloader to modify from " classloaders))))))) - -(defmethod solution-only "clojure" - [{:keys [:setup :solution]}] - (let [dir (TempDir/create "clojure") - solution-file (io/file dir "solution.clj")] - (when (not (empty? setup)) (util/write-code! "clojure" dir setup)) - (spit solution-file solution) - (add-classpath dir) - (load-file (str solution-file)))) - -(defmethod full-project "clojure" - [{:keys [:setup :solution :fixture]}] - (let [dir (TempDir/create "clojure") - {fixture-ns :class-name} - (util/write-code! "clojure" dir fixture)] - (when (not (empty? setup)) (util/write-code! "clojure" dir setup)) - (util/write-code! "clojure" dir solution) - (add-classpath dir) - (require fixture-ns) - (codewars.clojure.test/run-tests fixture-ns))) diff --git a/jvm-runner/src/clojure/codewars/runner.clj b/jvm-runner/src/clojure/codewars/runners.clj similarity index 96% rename from jvm-runner/src/clojure/codewars/runner.clj rename to jvm-runner/src/clojure/codewars/runners.clj index 52689e1d..4913a562 100644 --- a/jvm-runner/src/clojure/codewars/runner.clj +++ b/jvm-runner/src/clojure/codewars/runners.clj @@ -1,4 +1,4 @@ -(ns codewars.runner) +(ns codewars.runners) (defmulti solution-only :language) (defmulti full-project :language) diff --git a/jvm-runner/src/clojure/codewars/runners/clojure.clj b/jvm-runner/src/clojure/codewars/runners/clojure.clj new file mode 100644 index 00000000..2abec38d --- /dev/null +++ b/jvm-runner/src/clojure/codewars/runners/clojure.clj @@ -0,0 +1,35 @@ +(ns codewars.runners.clojure + (:require [codewars.runners :refer [solution-only full-project]] + [codewars.util :as util] + [codewars.clojure.test] + [clojure.java.io :as io]) + (:import [codewars.java TempDir]) + (:refer-clojure :exclude (add-classpath))) + +(defn- add-classpath + "Add a url path to the system class loader" + [new-classpath] + (let [field (aget (.getDeclaredFields java.net.URLClassLoader) 0)] + (.setAccessible field true) + (let [ucp (.get field (ClassLoader/getSystemClassLoader))] + (.addURL ucp (io/as-url new-classpath))))) + +(defmethod solution-only "clojure" + [{:keys [:setup :solution]}] + (let [dir (TempDir/create "clojure") + solution-file (io/file dir "solution.clj")] + (when (not (empty? setup)) (util/write-code! "clojure" dir setup)) + (spit solution-file solution) + (add-classpath dir) + (load-file (str solution-file)))) + +(defmethod full-project "clojure" + [{:keys [:setup :solution :fixture]}] + (let [dir (TempDir/create "clojure") + {fixture-ns :class-name} + (util/write-code! "clojure" dir fixture)] + (when (not (empty? setup)) (util/write-code! "clojure" dir setup)) + (util/write-code! "clojure" dir solution) + (add-classpath dir) + (require fixture-ns) + (codewars.clojure.test/run-tests fixture-ns))) diff --git a/jvm-runner/src/clojure/codewars/runners/groovy.clj b/jvm-runner/src/clojure/codewars/runners/groovy.clj new file mode 100644 index 00000000..68d085f4 --- /dev/null +++ b/jvm-runner/src/clojure/codewars/runners/groovy.clj @@ -0,0 +1,8 @@ +(ns codewars.runners.groovy + (:require [codewars.runners :refer [solution-only]]) + (:import [groovy.lang GroovyShell])) + +(defmethod solution-only "groovy" + [{:keys [:setup :solution]}] + (when (not (nil? setup)) (throw (Exception. "Setup code is not supported"))) + (.evaluate (GroovyShell.) solution)) diff --git a/jvm-runner/src/clojure/codewars/runner/java.clj b/jvm-runner/src/clojure/codewars/runners/java.clj similarity index 77% rename from jvm-runner/src/clojure/codewars/runner/java.clj rename to jvm-runner/src/clojure/codewars/runners/java.clj index 1cd13230..cb86c6a2 100644 --- a/jvm-runner/src/clojure/codewars/runner/java.clj +++ b/jvm-runner/src/clojure/codewars/runners/java.clj @@ -1,32 +1,35 @@ -(ns codewars.runner.java - (:require [codewars.runner :refer [solution-only full-project]] +(ns codewars.runners.java + (:require [codewars.runners :refer [solution-only full-project]] [codewars.clojure.test] [clojure.java.io :as io] [codewars.util :refer [write-code!]]) (:import [codewars.java TempDir] [java.net URLClassLoader] [javax.tools ToolProvider] + [java.io ByteArrayOutputStream] [org.junit.runner JUnitCore] - [codewars.java CwRunListener] - [java.io IOException])) + [codewars.java CwRunListener])) (defn- compile! "Compile files using the java compiler" [& files] - (let [compilation-result - (-> (ToolProvider/getSystemJavaCompiler) - ;; TODO: write compilation errors somewhere? - (.run nil nil nil (into-array (map str files))))] + (let [out-stream (ByteArrayOutputStream.) + err-stream (ByteArrayOutputStream.) + compilation-result + (. (ToolProvider/getSystemJavaCompiler) + run nil out-stream err-stream + (into-array (map str files)))] + (-> out-stream str print) (if (zero? compilation-result) 0 - (throw (IOException. "Java compilation error"))))) + (throw (RuntimeException. (str err-stream)))))) (defn- load-class "Load a java class in a specified directory" [dir class-name] (let [class-loader (URLClassLoader/newInstance - (into-array [(-> dir .toURI .toURL)]))] + (into-array [(io/as-url dir)]))] (Class/forName (name class-name) true class-loader))) (defn- run-junit-tests diff --git a/jvm-runner/src/clojure/codewars/util.clj b/jvm-runner/src/clojure/codewars/util.clj index 772c262a..ba5db10c 100644 --- a/jvm-runner/src/clojure/codewars/util.clj +++ b/jvm-runner/src/clojure/codewars/util.clj @@ -5,8 +5,12 @@ language-data {"clojure" {:pattern #"^\(ns\s+([A-Z|a-z](?:[a-z|A-Z|0-9|-]|\.[A-Z|a-z])*)\W" :extension "clj"} + "java" {:pattern #"\bclass\s+([A-Z][a-z|A-Z|0-9|_]*)\W" - :extension "java"}}) + :extension "java"} + + "groovy" {:pattern #"^package\s+([a-z](?:[a-z|A-Z|0-9|-]|\.[a-z])*)\W" + :extension "groovy"}}) (defn class-name "Infer the appropriate class or namespace name from code given a language" diff --git a/jvm-runner/test/codewars/core_test.clj b/jvm-runner/test/codewars/core_test.clj index 2ce77a27..ca54db67 100644 --- a/jvm-runner/test/codewars/core_test.clj +++ b/jvm-runner/test/codewars/core_test.clj @@ -2,7 +2,7 @@ (:require [clojure.test :refer :all] [codewars.core :refer [-main] :as core] [cheshire.core :as json] - [codewars.runner :refer [run]])) + [codewars.runners :refer [run]])) (deftest sanity-check (testing "-main is parsing JSONs from *in* and using handle to handle them" diff --git a/jvm-runner/test/codewars/runner/clojure_test.clj b/jvm-runner/test/codewars/runners/clojure_test.clj similarity index 99% rename from jvm-runner/test/codewars/runner/clojure_test.clj rename to jvm-runner/test/codewars/runners/clojure_test.clj index 6bbb7e4d..aa8fd746 100644 --- a/jvm-runner/test/codewars/runner/clojure_test.clj +++ b/jvm-runner/test/codewars/runners/clojure_test.clj @@ -1,4 +1,4 @@ -(ns codewars.runner.clojure-test +(ns codewars.runners.clojure-test (:require [clojure.test :refer :all] [codewars.core :refer [-main] :as core] [codewars.test.utils :refer [with-out-str-not-thread-safe]] diff --git a/jvm-runner/test/codewars/runners/groovy_test.clj b/jvm-runner/test/codewars/runners/groovy_test.clj new file mode 100644 index 00000000..8c48512c --- /dev/null +++ b/jvm-runner/test/codewars/runners/groovy_test.clj @@ -0,0 +1,21 @@ +(ns codewars.runners.groovy-test + (:require [clojure.test :refer :all] + [cheshire.core :as json] + [codewars.core :refer [-main] :as core] + [codewars.test.utils :refer [with-java-out-str]])) + +(deftest groovy-solution-only + (testing "-main can handle a groovy solution with no fixture" + (with-in-str + (json/generate-string + {:language "groovy" + :solution "1 + 1"}) + (is (= 2 (-main)))))) + +(deftest groovy-java-out + (testing "-main can handle a groovy solution with no setup code but no fixture" + (with-in-str + (json/generate-string + {:language "groovy" + :solution "print 'Hello Groovy!'"}) + (is (= "Hello Groovy!" (with-java-out-str (-main))))))) diff --git a/jvm-runner/test/codewars/runner/java_test.clj b/jvm-runner/test/codewars/runners/java_test.clj similarity index 85% rename from jvm-runner/test/codewars/runner/java_test.clj rename to jvm-runner/test/codewars/runners/java_test.clj index 251925d3..f85b77ac 100644 --- a/jvm-runner/test/codewars/runner/java_test.clj +++ b/jvm-runner/test/codewars/runners/java_test.clj @@ -1,7 +1,7 @@ -(ns codewars.runner.java-test +(ns codewars.runners.java-test (:require [clojure.test :refer :all] - [codewars.core :refer [-main]] - [codewars.test.utils :refer [with-java-out-str]] + [codewars.core :refer [-main] :as core] + [codewars.test.utils :refer :all] [cheshire.core :as json])) (deftest java-basic @@ -21,7 +21,6 @@ :solution "public class FooFighters {public static int main() {return 1;}}"}) (is (= 1 (-main)))))) - (deftest java-solution-print (testing "-main can handle a java solution that prints to standard out" (with-in-str @@ -90,6 +89,22 @@ (is (not (.contains test-out-string "Shouldn't get here"))) (is (not (.contains test-out-string "Test Passed<:LF:>"))))))) +(deftest java-bad-code + (testing "-main fails when code can't compile" + (with-in-str + (json/generate-string + {:language "java" + :solution "public class Solution { + public static void main(String[] args){ + notdefinedgonnafail(\"42\");}}"}) + (let [error-message + (with-redefs [core/fail #(-> % .getMessage)] (-main))] + (is (.contains error-message "error: cannot find symbol")) + (is (.contains error-message "notdefinedgonnafail(\"42\");")) + (is (.contains error-message "symbol: method notdefinedgonnafail(String)")) + (is (.contains error-message "location: class Solution")) + (is (.contains error-message "1 error")))))) + (deftest java-nine-yards (testing "-main can setup, solution, and test fixture code for java" (with-in-str diff --git a/jvm-runner/test/codewars/test/utils.clj b/jvm-runner/test/codewars/test/utils.clj index f36613a3..7f1b4d7b 100644 --- a/jvm-runner/test/codewars/test/utils.clj +++ b/jvm-runner/test/codewars/test/utils.clj @@ -19,3 +19,11 @@ (with-redefs [*out* s#] ~@body (str s#)))) + +(defmacro with-err-str + "A version of clojure.core/with-out-str for *err*" + [& body] + `(let [s# (new java.io.StringWriter)] + (binding [*err* s#] + ~@body + (str s#)))) diff --git a/lib/config.js b/lib/config.js index 47e3a6c5..2201c82f 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,5 +1,5 @@ module.exports = { - version: '0.1.27', + version: '0.1.29', testFramework: { defaults: { javascript: 'cw-2', diff --git a/lib/opts.js b/lib/opts.js index c7f16c2b..a8f99fdb 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -67,6 +67,10 @@ function processLanguage(opts, cb) opts.ext = 'cpp'; break; + case 'groovy': + opts.language = 'groovy'; + opts.ext = 'groovy'; + break; case 'jl': case 'julia': diff --git a/lib/runners/clojure.js b/lib/runners/clojure.js index ebd19b07..6f3b7cf1 100644 --- a/lib/runners/clojure.js +++ b/lib/runners/clojure.js @@ -1,38 +1,20 @@ -var shovel = require('../shovel'), - config = require('../config'), - codeWriteSync = require('../util').codeWriteSync, - glob = require('glob'), - fs = require('fs'), - path = require('path'), - temp = require('temp'); +var shovel = require('../shovel'); module.exports.run = function run(opts, cb) { - temp.track(); - var clojureCodeDir = temp.mkdirSync('clojure'), - classPath = glob.sync(path.join( - process.env.HOME,'.m2','repository','**','*.jar')); - classPath.push(clojureCodeDir); - classPath.push('frameworks/clojure'); - shovel.start(opts, cb, { solutionOnly: function () { - if (opts.setup) codeWriteSync('clojure', opts.setup, clojureCodeDir); return { name: 'java', - args: ['-XX:ErrorFile=/dev/null', '-cp', classPath.join(':'), 'clojure.main', - codeWriteSync('clojure', opts.solution, clojureCodeDir, "solution.clj")]}; + args: ['-jar', '/codewars/jvm-runner/target/jvm-runner-0.1.1-standalone.jar'], + stdin: JSON.stringify(opts) + }; }, fullProject: function () { - if (opts.setup) codeWriteSync('clojure', opts.setup, clojureCodeDir); - codeWriteSync('clojure', opts.solution, clojureCodeDir); return { name: 'java', - args: ['-XX:ErrorFile=/dev/null', '-cp', classPath.join(':'), 'clojure.main', - codeWriteSync( - 'clojure', - opts.fixture + config.snippets.clojure.runTests, - clojureCodeDir, - "test_fixture.clj")]}; + args: ['-jar', '/codewars/jvm-runner/target/jvm-runner-0.1.1-standalone.jar'], + stdin: JSON.stringify(opts) + }; } }); }; diff --git a/lib/runners/fsharp.js b/lib/runners/fsharp.js index f55c0ec9..e2503300 100644 --- a/lib/runners/fsharp.js +++ b/lib/runners/fsharp.js @@ -11,7 +11,7 @@ module.exports.run = function run(opts, cb) shovel.start(opts, cb, { solutionOnly: function () { - var file = util.codeWriteSync('fsharp', opts.solution, dir, 'solution.fsx') + var file = util.codeWriteSync('fsharp', opts.solution, dir, 'solution.fsx'); return {name: 'fsharpi', 'args': [file]}; }, fullProject: function () diff --git a/lib/runners/groovy.js b/lib/runners/groovy.js new file mode 120000 index 00000000..2869ff4f --- /dev/null +++ b/lib/runners/groovy.js @@ -0,0 +1 @@ +clojure.js \ No newline at end of file diff --git a/lib/runners/java.js b/lib/runners/java.js deleted file mode 100644 index a1c2e63e..00000000 --- a/lib/runners/java.js +++ /dev/null @@ -1,44 +0,0 @@ -var shovel = require('../shovel'), - config = require('../config'), - fs = require('fs'); - -module.exports.run = function run(opts, cb) { - //write the code to file - fs.writeFileSync('Solution.java', opts.solution); - if (opts.fixture) { - fs.writeFileSync('TestFixture.java', opts.fixture); - } - - //Compile the code - shovel.start(opts, cb, - { - compile: - { - solutionOnly: function () { - return {name: 'javac', 'args': ['Solution.java']}; - }, - fullProject: function () { - return {name: 'javac', 'args': ['-cp', - ['.', '/root/.m2/repository/junit/junit/4.11/*'].join(":"), - 'frameworks/java/CwRunListener.java', - 'frameworks/java/CwTestRunner.java', - 'Solution.java', - 'TestFixture.java']}; - } - }, - solutionOnly: function () { - return {name: 'java', 'args': ['-XX:ErrorFile=/dev/null', 'Solution']}; - }, - fullProject: function () { - return {name: 'java', 'args': [ - '-XX:ErrorFile=/dev/null', - '-cp', - ['.', - '/root/.m2/repository/junit/junit/4.11/*', - '/root/.m2/repository/org/hamcrest/hamcrest-core/1.3/*', - 'frameworks/java/'].join(':'), - 'CwTestRunner', - 'TestFixture']}; - } - }); -}; diff --git a/lib/runners/java.js b/lib/runners/java.js new file mode 120000 index 00000000..2869ff4f --- /dev/null +++ b/lib/runners/java.js @@ -0,0 +1 @@ +clojure.js \ No newline at end of file diff --git a/lib/runners/ruby.js b/lib/runners/ruby.js index 6ebc27b1..b1f33376 100644 --- a/lib/runners/ruby.js +++ b/lib/runners/ruby.js @@ -1,6 +1,6 @@ var shovel = require('../shovel'), util = require('../util'), - fs = require('fs'); + temp = require('temp'); module.exports.run = function run(opts, cb) { @@ -49,16 +49,14 @@ function prepareCw2(opts) function prepareRSpec(opts) { - var code = []; + temp.track(); + var dir = temp.mkdirSync('ruby'), + code = [opts.solution,opts.fixture]; if (opts.setup) - { - code.push(opts.setup); - } - code.push(opts.solution); - code.push(opts.fixture); + code.unshift(opts.setup); - fs.writeFileSync('solution.rb', code.join('\n')); + var solution = util.codeWriteSync('ruby', code.join('\n'), dir, 'solution.rb'); - return {name: 'rspec', 'args': ['solution.rb', '--require', './frameworks/ruby/cwrspecformatter.rb', '--format', 'CwRSpecFormatter']}; + return {name: 'rspec', 'args': [solution, '--require', './frameworks/ruby/cwrspecformatter.rb', '--format', 'CwRSpecFormatter']}; } diff --git a/lib/shovel.js b/lib/shovel.js index 1903bbb2..da4e8874 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -28,40 +28,13 @@ module.exports.start = function start(opts, cb, strategies) } }; -//runs the strategy specified in opts. -//If the strategy succeeds, silently runs the callback -//If the stragegy fails, reports the results of the run then aborts -// TODO: This method and its usage within the java runner are very confusing. Clean it up, or possibly remove it. -module.exports.compile = function compile(opts, cb, strategies) -{ - cb = cb || function (){}; - - var copts = {compiling: true}; - for(var key in opts) - { - copts[key] = opts[key]; - } - - run(copts, strategies, function (buffer) - { - if (buffer.exitCode === 0) - { - cb(buffer); - } - else - { - reportBuffer(opts, buffer); - } - }); -}; - // given the options provided and a list of strategies on how to handle them, run will pick the // appropriate strategy and execute it. function run(opts, strategies, cb) { function runStrategy(strategy) { - exec(opts, strategy.name, strategy.args, strategy.options, cb); + exec(opts, strategy.name, strategy.args, strategy.options, strategy.stdin, cb); } var strategy = (!opts.fixture) ? strategies.solutionOnly() : strategies.fullProject(); @@ -82,7 +55,7 @@ function run(opts, strategies, cb) } } -function exec(opts, name, args, process_options, cb) +function exec(opts, name, args, process_options, process_stdin, cb) { function exit(reason) { @@ -114,6 +87,9 @@ function exec(opts, name, args, process_options, cb) finished = false, stdoutLength = 0, maxTime = opts.timeout || config.timeouts[opts.language] || config.timeouts.default; + + if (process_stdin) child.stdin.write(process_stdin); + // Listen child.stdout.on('data', function (data) { @@ -200,4 +176,4 @@ function reportBuffer(opts, buffer, strategies) console.info(buffer.wallTime + 'ms'); } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index c1059d4a..7046caee 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "connect-timeout": "*", "dockerode": "2.0.x", "express": "*", - "glob": "*", "memory-streams": "*", "mocha": "1.19.x", "nomnom": "1.6.x", diff --git a/test/runners/clojure_spec.js b/test/runners/clojure_spec.js index 180693c5..787da30c 100644 --- a/test/runners/clojure_spec.js +++ b/test/runners/clojure_spec.js @@ -139,4 +139,16 @@ describe('clojure runner', function () { }); }); }); + describe('potpourri', function () { + it('test framework should not think HOME is /root', function (done) { + runner.run({ + language: 'clojure', + solution: '(print (System/getenv "HOME"))' + }, function (buffer) { + console.log(buffer.stderr); + expect(buffer.stdout).to.not.equal('/root'); + done(); + }); + }); + }); }); diff --git a/test/runners/erlang_spec.js b/test/runners/erlang_spec.js index bf15902b..e7a48bc1 100644 --- a/test/runners/erlang_spec.js +++ b/test/runners/erlang_spec.js @@ -29,6 +29,15 @@ describe('erlang runner', function () { done(); }); }); + it('should be running the most recent erlang version', function (done) { + runner.run({ + language: 'erlang', + solution: 'io:fwrite(erlang:system_info(otp_release)), init:stop().' + }, function (buffer) { + expect(buffer.stdout).to.equal('17'); + done(); + }); + }); }); describe('codewars test framework (eunit)', function () { it('should be able to run a basic test', function (done) { diff --git a/test/runners/groovy_spec.js b/test/runners/groovy_spec.js new file mode 100644 index 00000000..02af2353 --- /dev/null +++ b/test/runners/groovy_spec.js @@ -0,0 +1,14 @@ +var expect = require('chai').expect; +var runner = require('../../lib/runners/groovy'); + + +describe( 'groovy runner', function(){ + describe( '.run', function(){ + it( 'should handle basic code evaluation', function(done){ + runner.run({language: 'groovy', solution: "println '42'"}, function(buffer) { + expect(buffer.stdout).to.equal('42\n'); + done(); + }); + }); + }); +}); diff --git a/test/runners/java_spec.js b/test/runners/java_spec.js index 7481c58d..8a502926 100644 --- a/test/runners/java_spec.js +++ b/test/runners/java_spec.js @@ -2,69 +2,70 @@ var expect = require('chai').expect; var runner = require('../../lib/runners/java'); -describe( 'java runner', function(){ - describe( '.run', function(){ - it( 'should handle basic code evaluation', function(done){ +describe('java runner', function () { + describe('.run', function () { + it('should handle basic code evaluation', function (done) { runner.run({language: 'java', - solution: 'class Solution {\n' - + ' public static void main(String[] args){\n' - + ' System.out.println("42");\n' - + ' }\n' - + '}\n' - }, function(buffer) { - expect(buffer.stdout ).to.equal('42\n'); + solution: [ + 'public class Solution {', + ' public static void main(){', + ' System.out.println("42");', + ' }', + '}'].join('\n') + }, function (buffer) { + expect(buffer.stdout).to.contain('42\n'); done(); }); }); }); - describe( 'junit', function(){ - it('should handle basic junit tests', function(done){ + describe('junit', function () { + it('should handle basic junit tests', function (done) { runner.run({language: 'java', - solution: 'public class Solution {\n' - + ' public Solution(){}\n' - + ' public int testthing(){return 3;}\n' - + ' public static void main(String[] args){\n' - + ' System.out.println("42");\n' - + ' }\n' - + '}\n', - fixture: 'import static org.junit.Assert.assertEquals;\n' - + 'import org.junit.Test;\n' - + 'import org.junit.runners.JUnit4;\n' - + 'public class TestFixture {\n' - + ' public TestFixture(){}' - + ' @Test\n' - + ' public void myTestFunction(){\n' - + ' Solution s = new Solution();\n' - + ' assertEquals("wow", 3, s.testthing());\n' - + ' System.out.println("test out");\n' - + '}}' - }, function(buffer) { - expect(buffer.stdout ).to.equal('myTestFunction(TestFixture)\ntest out\nTest Passed\n'); + solution: 'public class Solution {\n' + + ' public Solution(){}\n' + + ' public int testthing(){return 3;}\n' + + ' public static void main(String[] args){\n' + + ' System.out.println("42");\n' + + ' }\n' + + '}\n', + fixture: 'import static org.junit.Assert.assertEquals;\n' + + 'import org.junit.Test;\n' + + 'import org.junit.runners.JUnit4;\n' + + 'public class TestFixture {\n' + + ' public TestFixture(){}' + + ' @Test\n' + + ' public void myTestFunction(){\n' + + ' Solution s = new Solution();\n' + + ' assertEquals("wow", 3, s.testthing());\n' + + ' System.out.println("test out");\n' + + '}}' + }, function (buffer) { + expect(buffer.stdout).to.contain('myTestFunction(TestFixture)<:LF:>\ntest out\nTest Passed<:LF:>\n'); done(); }); }); - it('should handle junit tests failing', function(done){ + it('should handle junit tests failing', function (done) { runner.run({language: 'java', - solution: 'public class Solution {\n' - + ' public Solution(){}\n' - + ' public int testthing(){return 3;}\n' - + ' public static void main(String[] args){\n' - + ' System.out.println("42");\n' - + ' }\n' - + '}\n', - fixture: 'import static org.junit.Assert.assertEquals;\n' - + 'import org.junit.Test;\n' - + 'import org.junit.runners.JUnit4;\n' - + 'public class TestFixture {\n' - + ' public TestFixture(){}' - + ' @Test\n' - + ' public void myTestFunction(){\n' - + ' Solution s = new Solution();\n' - + ' assertEquals("Failed Message", 5, s.testthing());\n' - + ' System.out.println("test out");\n' - + '}}' - }, function(buffer) { - expect(buffer.stdout ).to.equal('myTestFunction(TestFixture)\nFailed Message expected:<5> but was:<3>\n'); + solution: 'public class Solution {\n' + + ' public Solution(){}\n' + + ' public int testthing(){return 3;}\n' + + ' public static void main(String[] args){\n' + + ' System.out.println("42");\n' + + ' }\n' + + '}\n', + fixture: 'import static org.junit.Assert.assertEquals;\n' + + 'import org.junit.Test;\n' + + 'import org.junit.runners.JUnit4;\n' + + 'public class TestFixture {\n' + + ' public TestFixture(){}' + + ' @Test\n' + + ' public void myTestFunction(){\n' + + ' Solution s = new Solution();\n' + + ' assertEquals("Failed Message", 5, s.testthing());\n' + + ' System.out.println("test out");\n' + + '}}' + }, function (buffer) { + expect(buffer.stdout).to.contain('myTestFunction(TestFixture)<:LF:>\nFailed Message expected:<5> but was:<3><:LF:>\n'); done(); }); }); diff --git a/test/runners/python_spec.js b/test/runners/python_spec.js index c21e1e3a..571d77e4 100644 --- a/test/runners/python_spec.js +++ b/test/runners/python_spec.js @@ -1,70 +1,72 @@ var expect = require('chai').expect; var runner = require('../../lib/runners/python'); -describe( 'python runner2', function(){ +describe('python runner2', function () { this.timeout(600); - describe( '.run', function(){ - it( 'should handle basic code evaluation', function(done){ - runner.run({language: 'python', solution: 'print 42'}, function(buffer) { + describe('.run', function () { + it('should handle basic code evaluation', function (done) { + runner.run({language: 'python', solution: 'print 42'}, function (buffer) { expect(buffer.stdout).to.equal('42\n'); done(); }); }); }); - describe( 'cw-2', function(){ - it( 'should handle a basic assertion', function(done){ - runner.run({language: 'python', - solution: 'a = 1', - fixture: 'test.expect(a == 1)', - testFramework: 'cw-2'}, - function(buffer){ - console.log(buffer); + describe('cw-2', function () { + it('should handle a basic assertion', function (done) { + runner.run({ + language: 'python', + solution: 'a = 1', + fixture: 'test.expect(a == 1)', + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); - done(); - }); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); }); - it( 'should handle a basic assert_equals', function(done){ + it('should handle a basic assert_equals', function (done) { runner.run({language: 'python', - solution: 'a = 1', - fixture: 'test.assert_equals(a, 1)', - testFramework: 'cw-2'}, - function(buffer){ - console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); - done(); - }); + solution: 'a = 1', + fixture: 'test.assert_equals(a, 1)', + testFramework: 'cw-2'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); }); - it( 'should handle a basic setup', function(done){ + it('should handle a basic setup', function (done) { runner.run({language: 'python', solution: 'a = 1', setup: 'b = 2', fixture: 'test.assert_equals(b, 2)', testFramework: 'cw-2'}, - function(buffer){ + function (buffer) { console.log(buffer); expect(buffer.stdout).to.equal('Test Passed\n'); done(); }); }); - it( 'should handle a failed assertion', function(done){ + it('should handle a failed assertion', function (done) { runner.run({language: 'python', - solution: 'a = 1', - fixture: 'test.expect(a == 2)', - testFramework: 'cw-2'}, - function(buffer){ - console.log(buffer); - expect(buffer.stdout).to.equal('Value is not what was expected\n'); - done(); - }); + solution: 'a = 1', + fixture: 'test.expect(a == 2)', + testFramework: 'cw-2'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Value is not what was expected\n'); + done(); + }); }); - it( 'should handle a failed assertion', function(done){ + it('should handle a failed assertion', function (done) { runner.run({language: 'python', solution: 'a.fail()', testFramework: 'cw-2'}, - function(buffer){ + function (buffer) { console.log(buffer); expect(buffer.stderr).to.not.contain('File '); expect(buffer.stderr).to.not.contain(', line '); @@ -73,49 +75,88 @@ describe( 'python runner2', function(){ }); }); }); - describe( 'unittest', function(){ - it( 'should handle a basic assertion', function(done){ + describe('unittest', function () { + it('should handle a basic assertion', function (done) { runner.run({language: 'python', - solution: 'a = 1', - fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' - + ' def test_assert(self):\n' - + ' self.assertEqual(a, 1)\n' - + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', - testFramework: 'unittest'}, - function(buffer){ - console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); - done(); - }); + solution: 'a = 1', + fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' + + ' def test_assert(self):\n' + + ' self.assertEqual(a, 1)\n' + + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); }); - it( 'should handle a failed assetion', function(done){ + it('should handle a failed assetion', function (done) { runner.run({language: 'python', - solution: 'a = 1', - fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' - + ' def test_assert(self):\n' - + ' self.assertEqual(a, 2, "test failed")\n' - + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', - testFramework: 'unittest'}, - function(buffer){ - console.log(buffer); - expect(buffer.stdout).to.equal('test failed\n'); - done(); - }); + solution: 'a = 1', + fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' + + ' def test_assert(self):\n' + + ' self.assertEqual(a, 2, "test failed")\n' + + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('test failed\n'); + done(); + }); }); - it( 'should handle a failed assetion', function(done){ + it('should handle a failed assetion', function (done) { runner.run({language: 'python', - solution: 'a = 1', - fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' - + ' def test_assert(self):\n' - + ' raise StandardError("exception")\n' - + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', - testFramework: 'unittest'}, - function(buffer){ - console.log(buffer); - expect(buffer.stdout).to.equal('Unhandled Exception: exception\n'); - done(); - }); + solution: 'a = 1', + fixture: 'class TestSequenceFunctions(unittest.TestCase):\n' + + ' def test_assert(self):\n' + + ' raise StandardError("exception")\n' + + '_testsuite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)', + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Unhandled Exception: exception\n'); + done(); + }); + }); + }); + describe('potpourri', function () { + it('can run mongodb', function (done) { + this.timeout(5000); + runner.run({ + language: 'python', + setup: [ + 'import subprocess', + 'import re', + 'mongod = subprocess.Popen("mongod", stdout=subprocess.PIPE, stderr=subprocess.STDOUT)', + 'while True:', + ' l = mongod.stdout.readline()', + ' m = re.match(r".*waiting for connections on port 27017", l)', + ' if m:', + ' print l', + ' break' + ].join('\n'), + solution: [ + 'from pymongo import MongoClient', + 'with MongoClient() as client:', + ' table = client["HelloMongoCollection"]["HelloMongoTable"]', + ' table.drop()', + " table.insert({'_id': 42, 'name': 'My Document', 'ids': [1,2,3], 'subdocument': {'a':2}})" + ].join('\n'), + fixture: [ + 'from pymongo import MongoClient', + 'with MongoClient() as client:', + ' table = client["HelloMongoCollection"]["HelloMongoTable"]', + " test.assert_equals(list(table.find()), [{u'_id': 42, u'name': u'My Document', u'subdocument': {u'a': 2}, u'ids': [1, 2, 3]}])", + 'mongod.terminate()' + ].join('\n'), + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.contain('waiting for connections on port 27017'); + expect(buffer.stdout).to.contain('Test Passed'); + done(); + }); }); }); - }); diff --git a/test/runners/ruby_spec.js b/test/runners/ruby_spec.js index 26377f7b..2e99a384 100644 --- a/test/runners/ruby_spec.js +++ b/test/runners/ruby_spec.js @@ -2,69 +2,69 @@ var expect = require('chai').expect; var runner = require('../../lib/runners/ruby'); -describe( 'ruby runner', function(){ - describe( '.run', function(){ - it( 'should handle basic code evaluation', function(done){ - runner.run({language: 'ruby', solution: 'puts 42'}, function(buffer) { - expect(buffer.stdout ).to.equal('42\n'); +describe('ruby runner', function () { + describe('.run', function () { + it('should handle basic code evaluation', function (done) { + runner.run({language: 'ruby', solution: 'puts 42'}, function (buffer) { + expect(buffer.stdout).to.equal('42\n'); done(); }); }); }); - describe('cw-2', function() { - it( 'should handle a basic assertion', function(done){ - runner.run({language: 'ruby', solution: 'a = 1', fixture: 'Test.expect a == 1', testFramework: 'cw-2'}, function(buffer) { - expect(buffer.stdout ).to.equal('Test Passed\n'); + describe('cw-2', function () { + it('should handle a basic assertion', function (done) { + runner.run({language: 'ruby', solution: 'a = 1', fixture: 'Test.expect a == 1', testFramework: 'cw-2'}, function (buffer) { + expect(buffer.stdout).to.equal('Test Passed\n'); done(); }); }); - it( 'should handle a basic description', function(done){ - runner.run({language: 'ruby', solution: 'a = 1', fixture: 'describe("test") { Test.expect a == 1 }', testFramework: 'cw-2'}, function(buffer) { + it('should handle a basic description', function (done) { + runner.run({language: 'ruby', solution: 'a = 1', fixture: 'describe("test") { Test.expect a == 1 }', testFramework: 'cw-2'}, function (buffer) { expect(buffer.stdout).to.contain('test\nTest Passed\n'); expect(buffer.stdout).to.contain('ms'); done(); }); }); - describe('error handling', function() { - it( 'should handle a mix of failures and successes', function(done) { + describe('error handling', function () { + it('should handle a mix of failures and successes', function (done) { runner.run({language: 'ruby', - solution:'a = 1', + solution: 'a = 1', fixture: 'describe "test" do\n' + 'it("test1") { Test.expect(false) }\n' + 'it("test2") { Test.expect(true) }\n' + 'end', - testFramework: 'cw-2'}, function(buffer) { + testFramework: 'cw-2'}, function (buffer) { console.log(buffer.stdout) expect(buffer.stdout).to.contain('Value is not what was expected'); expect(buffer.stdout).to.contain('Test Passed'); done(); }); }); - it( 'should gracefully handle custom errors', function(done) { + it('should gracefully handle custom errors', function (done) { runner.run({language: 'ruby', - solution:'a = 1', + solution: 'a = 1', fixture: 'describe "test" do\n' + 'it("test1") { raise "boom!" }\n' + 'it("test2") { Test.expect(true)}\n' + 'end', - testFramework: 'cw-2'}, function(buffer) { + testFramework: 'cw-2'}, function (buffer) { expect(buffer.stdout).to.contain(''); expect(buffer.stdout).to.contain('boom!'); expect(buffer.stdout).to.contain('Test Passed'); done(); }); }); - it( 'should gracefully handle reference errors', function(done) { + it('should gracefully handle reference errors', function (done) { runner.run({language: 'ruby', - solution:'a = 1', + solution: 'a = 1', fixture: 'describe "test" do\n' + 'it("test1") { a.idontexist() }\n' + 'it("test2") { Test.expect(true)}\n' + 'end', - testFramework: 'cw-2'}, function(buffer) { + testFramework: 'cw-2'}, function (buffer) { expect(buffer.stdout).to.contain(''); expect(buffer.stdout).to.contain('<:LF:>'); expect(buffer.stdout).to.contain('NoMethodError:'); @@ -76,7 +76,7 @@ describe( 'ruby runner', function(){ }); }); - it('should prevent short circuiting', function(done){ + it('should prevent short circuiting', function (done) { runner.run({language: 'ruby', solution: [ "def example", @@ -90,8 +90,7 @@ describe( 'ruby runner', function(){ ' it("test2") { Test.expect(false)}', 'end' ].join('\n'), - testFramework: 'cw-2'}, function(buffer) - { + testFramework: 'cw-2'}, function (buffer) { expect(buffer.stdout).to.contain(''); done(); } @@ -99,28 +98,26 @@ describe( 'ruby runner', function(){ }); }); }); - describe('rspec', function() { - it('should handle a basic assertion', function(done){ + describe('rspec', function () { + it('should handle a basic assertion', function (done) { runner.run({language: 'ruby', - solution: 'a = 1', - fixture: 'describe "test" do\n' + - 'it("test2") { expect(1).to eq(1)}\n' + - 'end', - testFramework: 'rspec'}, function(buffer) - { + solution: 'a = 1', + fixture: 'describe "test" do\n' + + 'it("test2") { expect(1).to eq(1)}\n' + + 'end', + testFramework: 'rspec'}, function (buffer) { expect(buffer.stdout).to.equal('test\ntest2\nTest Passed\n'); done(); } ); }); - it('should handle a basic failed assertion', function(done){ + it('should handle a basic failed assertion', function (done) { runner.run({language: 'ruby', - solution: 'a = 1', - fixture: 'describe "test" do\n' + - 'it("test2") { expect(1).to eq(2)}\n' + - 'end', - testFramework: 'rspec'}, function(buffer) - { + solution: 'a = 1', + fixture: 'describe "test" do\n' + + 'it("test2") { expect(1).to eq(2)}\n' + + 'end', + testFramework: 'rspec'}, function (buffer) { expect(buffer.stdout).to.contain('test\ntest2'); expect(buffer.stdout).to.contain(''); expect(buffer.stdout).to.not.contain(''); @@ -128,15 +125,14 @@ describe( 'ruby runner', function(){ } ); }); - it('should handle a basic assertion', function(done){ + it('should handle a basic assertion', function (done) { runner.run({language: 'ruby', - solution: 'a = 1', - fixture: 'describe "test" do\n' + - 'it("test1") { a.idontexist() }\n' + - 'it("test2") { expect(true)}\n' + - 'end', - testFramework: 'rspec'}, function(buffer) - { + solution: 'a = 1', + fixture: 'describe "test" do\n' + + 'it("test1") { a.idontexist() }\n' + + 'it("test2") { expect(true)}\n' + + 'end', + testFramework: 'rspec'}, function (buffer) { expect(buffer.stdout).to.contain('test'); expect(buffer.stdout).to.contain('test1'); expect(buffer.stdout).to.contain('test2'); @@ -145,26 +141,46 @@ describe( 'ruby runner', function(){ } ); }); - it('should prevent short circuiting', function(done){ + it('should prevent short circuiting', function (done) { runner.run({language: 'ruby', - solution: [ - "def example", - " expect(true);", - " raise 'early error'", - "end" - ].join("\n"), - fixture: [ - 'describe "test" do', - ' it("test1") { example }', - ' it("test2") { expect(false)}', - 'end' - ].join('\n'), - testFramework: 'rspec'}, function(buffer) - { + solution: [ + "def example", + " expect(true);", + " raise 'early error'", + "end" + ].join("\n"), + fixture: [ + 'describe "test" do', + ' it("test1") { example }', + ' it("test2") { expect(false)}', + 'end' + ].join('\n'), + testFramework: 'rspec'}, function (buffer) { expect(buffer.stdout).to.contain(''); done(); } ); }); }); + describe('potpourri', function () { + it('can run reddis', function (done) { + runner.run({ + language: 'ruby', + solution: [ + 'fork do', + ' exec "redis-server"', + 'end', + "require 'redis'", + 'r = Redis.new', + "r.set('a', 'b')" + ].join('\n'), + fixture: "Test.assert_equals(r.get('a'), 'b')", + testFramework: 'cw-2' + }, function (buffer) { + console.log(buffer.stderr); + expect(buffer.stdout).to.contain('Test Passed: Value == \"b\"'); + done(); + }); + }); + }); });