From 40cc7747a9a8e44d816deb00795d16d865bf6db8 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Tue, 12 Aug 2014 19:50:20 -0500 Subject: [PATCH 01/11] Moving to latest erlang version --- Dockerfile | 23 +++++++++++++++++------ test/runners/erlang_spec.js | 9 +++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index f76a4a24..7cc7851c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,14 @@ # 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 + RUN apt-get install -y python python-dev python-pip python-virtualenv # Define mountable directories. @@ -73,7 +81,7 @@ 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 apt-get install -y ghc cabal-install RUN cabal update RUN cabal install hspec @@ -90,7 +98,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 @@ -105,9 +116,6 @@ RUN rm godeb # 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,7 +143,6 @@ 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 @@ -177,10 +184,14 @@ RUN apt-get -y install tcc RUN add-apt-repository ppa:ubuntu-toolchain-r/ppa RUN apt-get -y install clang-3.4 lldb-3.4 +# ADD codewarrior user +RUN useradd -s /usr/sbin/nologin codewarrior + # ADD cli-runner and install node deps ADD . /codewars WORKDIR /codewars RUN npm install +#USER codewarrior RUN mocha -t 5000 test/* #timeout is a fallback in case an error with node 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) { From d272c527c03f56909b70a9373013642fb6ec83d3 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 09:44:14 -0500 Subject: [PATCH 02/11] - Switching "runner" to "runners" to mirror node.js - No more dynapath - Java compiler throws compilation errors now when failing - Also introducing testing for this --- jvm-runner/project.clj | 1 - jvm-runner/src/clojure/codewars/core.clj | 13 +++-- .../src/clojure/codewars/runner/clojure.clj | 49 ------------------- .../codewars/{runner.clj => runners.clj} | 2 +- .../src/clojure/codewars/runners/clojure.clj | 35 +++++++++++++ .../codewars/{runner => runners}/java.clj | 23 +++++---- jvm-runner/test/codewars/core_test.clj | 2 +- .../{runner => runners}/clojure_test.clj | 2 +- .../{runner => runners}/java_test.clj | 23 +++++++-- jvm-runner/test/codewars/test/utils.clj | 8 +++ 10 files changed, 86 insertions(+), 72 deletions(-) delete mode 100644 jvm-runner/src/clojure/codewars/runner/clojure.clj rename jvm-runner/src/clojure/codewars/{runner.clj => runners.clj} (96%) create mode 100644 jvm-runner/src/clojure/codewars/runners/clojure.clj rename jvm-runner/src/clojure/codewars/{runner => runners}/java.clj (77%) rename jvm-runner/test/codewars/{runner => runners}/clojure_test.clj (99%) rename jvm-runner/test/codewars/{runner => runners}/java_test.clj (85%) diff --git a/jvm-runner/project.clj b/jvm-runner/project.clj index b8d8ede7..e2356436 100644 --- a/jvm-runner/project.clj +++ b/jvm-runner/project.clj @@ -4,7 +4,6 @@ :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"] [environ "0.5.0"]] :plugins [[lein-environ "0.5.0"]] diff --git a/jvm-runner/src/clojure/codewars/core.clj b/jvm-runner/src/clojure/codewars/core.clj index 8481a311..0f116fa9 100644 --- a/jvm-runner/src/clojure/codewars/core.clj +++ b/jvm-runner/src/clojure/codewars/core.clj @@ -1,10 +1,10 @@ (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.java] + [codewars.runners.clojure]) (:import [java.util.concurrent TimeoutException]) (:gen-class)) @@ -15,12 +15,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/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/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/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#)))) From c567e0096f35e11e19cdb22ee54f75a88f5325d7 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 09:45:01 -0500 Subject: [PATCH 03/11] Switching node to use jvm-runner --- Dockerfile | 20 ++-- frameworks/clojure/clojure/test/codewars.clj | 79 -------------- frameworks/java/CwRunListener.java | 29 ----- frameworks/java/CwTestRunner.java | 11 -- package.json | 1 - test/runners/java_spec.js | 107 ++++++++++--------- 6 files changed, 66 insertions(+), 181 deletions(-) delete mode 100644 frameworks/clojure/clojure/test/codewars.clj delete mode 100644 frameworks/java/CwRunListener.java delete mode 100644 frameworks/java/CwTestRunner.java diff --git a/Dockerfile b/Dockerfile index 7cc7851c..dae685a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,13 +72,9 @@ 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 apt-get install -y ghc cabal-install @@ -109,9 +105,10 @@ 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 +RUN apt-get install -y golang +#RUN curl https://godeb.s3.amazonaws.com/godeb-amd64.tar.gz | tar zxv +#RUN ./godeb install 1.3.1 +#RUN rm godeb # Install TypeScript RUN npm -g install typescript @@ -189,9 +186,16 @@ RUN useradd -s /usr/sbin/nologin codewarrior # ADD cli-runner and install node deps ADD . /codewars + +# Build the jvm-runner +WORKDIR /codewars/jvm-runner +RUN LEIN_ROOT=true lein do clean, test, uberjar + WORKDIR /codewars RUN npm install #USER codewarrior + +# Run the test suite to make sure this thing works 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/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/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(); }); }); From 0135a8401ad7f2f61cfd9ba3a3a35c4cb5c1f8d9 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 12:47:17 -0500 Subject: [PATCH 04/11] - Only compiling when we have to - codewarrior environment set up properly --- jvm-runner/Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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"] From 2c5417ffd0148a62ff3cf5e5c1292f940cb0592f Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 12:48:01 -0500 Subject: [PATCH 05/11] No longer running container as root, all tests passing :D --- Dockerfile | 54 +++++++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index dae685a0..3cc92e66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,24 +13,18 @@ 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"] @@ -48,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 @@ -78,8 +68,8 @@ RUN chmod a+x /usr/bin/lein # Install Haskell RUN apt-get install -y ghc cabal-install -RUN cabal update -RUN cabal install hspec +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) @@ -105,10 +95,11 @@ 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 apt-get install -y golang +# 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 @@ -146,7 +137,7 @@ RUN gem install rails --no-ri --no-rdoc 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 && \ @@ -154,8 +145,7 @@ RUN apt-get install -y mongodb-server && \ mkdir /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 @@ -164,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 @@ -181,21 +171,23 @@ RUN apt-get -y install tcc RUN add-apt-repository ppa:ubuntu-toolchain-r/ppa RUN apt-get -y install clang-3.4 lldb-3.4 -# ADD codewarrior user -RUN useradd -s /usr/sbin/nologin codewarrior - # ADD cli-runner and install node deps ADD . /codewars # Build the jvm-runner WORKDIR /codewars/jvm-runner -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 WORKDIR /codewars RUN npm install -#USER codewarrior # 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 From 43bb96e92f84e95d1918e9c43fb01299cb21f4fa Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 12:48:40 -0500 Subject: [PATCH 06/11] Added test to verify that we aren't root, need another test to check user via shell... --- test/runners/clojure_spec.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/runners/clojure_spec.js b/test/runners/clojure_spec.js index 180693c5..bb1ec313 100644 --- a/test/runners/clojure_spec.js +++ b/test/runners/clojure_spec.js @@ -139,4 +139,26 @@ 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(); + }); + }); +// it('test framework should be able to shell out and ask who the current user is', function (done) { +// runner.run({ +// language: 'clojure', +// solution: '(use \'[clojure.java.shell :only [sh]]) (sh "whoami")' +// }, function (buffer) { +// console.log(buffer.stderr); +// expect(buffer.stdout).to.equal('/root'); +// done(); +// }); +// }); + }); }); From 4d18f30d5caa5d2ebb0af30747e0fdbe3ea1c6ef Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 14:23:25 -0500 Subject: [PATCH 07/11] Cleaning up dead code --- test/runners/clojure_spec.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/runners/clojure_spec.js b/test/runners/clojure_spec.js index bb1ec313..787da30c 100644 --- a/test/runners/clojure_spec.js +++ b/test/runners/clojure_spec.js @@ -150,15 +150,5 @@ describe('clojure runner', function () { done(); }); }); -// it('test framework should be able to shell out and ask who the current user is', function (done) { -// runner.run({ -// language: 'clojure', -// solution: '(use \'[clojure.java.shell :only [sh]]) (sh "whoami")' -// }, function (buffer) { -// console.log(buffer.stderr); -// expect(buffer.stdout).to.equal('/root'); -// done(); -// }); -// }); }); }); From a3436e6c7664bb884e5bfc3a405a86a34a727428 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Wed, 13 Aug 2014 14:23:46 -0500 Subject: [PATCH 08/11] Adding test for reddis --- test/runners/ruby_spec.js | 140 +++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 62 deletions(-) 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(); + }); + }); + }); }); From c382c26fffd6b5e1489bd6ff7dcaff6fcf0a991c Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Thu, 14 Aug 2014 08:12:08 -0500 Subject: [PATCH 09/11] Groovy support in jvm-runner --- jvm-runner/project.clj | 1 + jvm-runner/src/clojure/codewars/core.clj | 5 +++-- .../src/clojure/codewars/runners/groovy.clj | 8 +++++++ jvm-runner/src/clojure/codewars/util.clj | 6 +++++- .../test/codewars/runners/groovy_test.clj | 21 +++++++++++++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 jvm-runner/src/clojure/codewars/runners/groovy.clj create mode 100644 jvm-runner/test/codewars/runners/groovy_test.clj diff --git a/jvm-runner/project.clj b/jvm-runner/project.clj index e2356436..cd6966b9 100644 --- a/jvm-runner/project.clj +++ b/jvm-runner/project.clj @@ -5,6 +5,7 @@ :dependencies [[org.clojure/clojure "1.6.0"] [cheshire "5.3.1"] [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 0f116fa9..fbfec5ac 100644 --- a/jvm-runner/src/clojure/codewars/core.clj +++ b/jvm-runner/src/clojure/codewars/core.clj @@ -3,8 +3,9 @@ [codewars.runners :refer [run]] [codewars.kill-switch :refer [with-timeout]] [environ.core :refer [env]] - [codewars.runners.java] - [codewars.runners.clojure]) + [codewars.runners.groovy] + [codewars.runners.clojure] + [codewars.runners.java]) (:import [java.util.concurrent TimeoutException]) (:gen-class)) 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/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/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))))))) From 07abaa60051828bcc77246863f664924d00610f6 Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Thu, 14 Aug 2014 12:14:46 -0500 Subject: [PATCH 10/11] Can run groovy now --- test/runners/groovy_spec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test/runners/groovy_spec.js 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(); + }); + }); + }); +}); From 9516ffce70c64dd5878d22ab75d9ac81c55011af Mon Sep 17 00:00:00 2001 From: Matthew Wampler-Doty Date: Thu, 14 Aug 2014 15:18:09 -0500 Subject: [PATCH 11/11] Mongodb works even without root access --- Dockerfile | 4 +- test/runners/python_spec.js | 191 ++++++++++++++++++++++-------------- 2 files changed, 118 insertions(+), 77 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3cc92e66..79e2e6b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -141,8 +141,8 @@ 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 -g install mongoose mongodb 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(); + }); }); }); - });