Skip to content

Commit

Permalink
Start of enhancements to Rust language support, issue codewars#128
Browse files Browse the repository at this point in the history
- Fixed up dockerfile
- Change runner to use cargo
- Setup basic tests and mappings
  • Loading branch information
lteacher committed Jul 9, 2016
1 parent 099f612 commit f20881e
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ HOSTNAME=codewars

# Building haskell and erlang images have been suspended (frozen) until they are able to be repaired
# CONTAINERS=dotnet jvm node python ruby alt func dart systems erlang haskell
CONTAINERS=dotnet jvm node python ruby alt func dart systems
CONTAINERS=dotnet jvm node python ruby alt func dart systems rust

ALL_CONTAINERS=${CONTAINERS} base

Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ services:
entrypoint: ''
command: bash

rust-runner:
image: codewars/rust-runner
volumes:
- ./lib:/runner/lib
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
entrypoint: ''
command: bash

# LANGUAGE SPECIFIC HELPERS
javascript:
image: codewars/node-runner
Expand Down
27 changes: 27 additions & 0 deletions docker/rust.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM codewars/base-runner

# Install Rust Compiler
RUN curl -sSf https://static.rust-lang.org/rustup.sh | sh

# Setup env
ENV USER codewarrior
ENV HOME /home/codewarrior

# Copy package json to tmp
ADD package.json /tmp/package.json

# Do npm install in tmp and setup cargo dir
RUN \
cd /tmp && npm install --production && \
mkdir -p /runner && cp -a /tmp/node_modules /runner

# ADD cli-runner
ADD . /runner

# Working dir is /runner
WORKDIR /runner

# Codewarrior...
USER codewarrior

RUN mocha -t 10000 test/runners/rust_spec.js
126 changes: 96 additions & 30 deletions lib/runners/rust.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,98 @@
var shovel = require('../shovel'),
util = require('../util'),
path = require('path'),
temp = require('temp'),
exec = require('child_process').exec;

function compile(args, cb) {
args.unshift('rustc');
exec(args.join(' '), cb);
}
'use strict'

const shovel = require('../shovel');
const util = require('../util');
const fs = require('fs');
const path = require('path');
const exec = require('child_process').exec;
// const temp = require('temp');
const temp = require('temp').track();

const dir = temp.mkdirSync('rust');

module.exports.run = function run(opts, cb) {
temp.track();
var dir = temp.mkdirSync('rust');

shovel.start(opts, cb, {
solutionOnly: function (run) {
var executable = path.join(dir, 'solution'),
solutionFile = util.codeWriteSync('rust', opts.solution, dir, 'solution.rs'),
args = [solutionFile, '-o', executable];
compile(args, function (error, stdout, stderr) {
if (stdout) console.log(stdout);
if (stderr) throw new Error(stderr);
if (error !== null) throw error;
run({'name': executable, 'args': []});
});
},
testIntegration: function (run) {
throw new Error('Test framework is not supported');
}
});
};
shovel.start(opts, cb, {
solutionOnly: function(run) {
_init(err => {
let file = _createSolution(opts);

// Run
run({
name: 'cargo',
args: ['run'],
options: {
cwd: dir
}
});
});
},
testIntegration: function(run) {
// test is the only one supported right now...
if (opts.testFramework != 'cargo')
throw 'Test framework is not supported';
_init(err => {
let file = _createSpec(opts);

// Run
run({
name: 'cargo',
args: ['test'],
options: {
cwd: dir
}
});
});

},
sanitizeStdOut: function(stdout) {
if (opts.fixture) return _format(stdout)
else return stdout;
}
});
};

// Create the solution file
const _createSolution = function(opts) {
// Set the code
let code = `
${opts.setup ? opts.setup : ''}
${opts.code}
`;

return util.codeWriteSync('rust', code, `${dir}/src`,'main.rs', true);
}

// Create the spec
const _createSpec = function(opts) {
let code = `
${opts.setup ? opts.setup : ''}
${opts.code}
${opts.fixture}
`;

return util.codeWriteSync('rust', code, `${dir}/src`,'main.rs', true);
}

// Initalizes the Rust dir via cargo
const _init = function(cb) {
exec(`cargo init --bin`,{cwd: dir},cb);
}

const _format = function(stdout) {
let output = '';
let tests = stdout.split(/\n/gm).filter(v => !v.search(/^test.*(?:ok|FAILED)$/));

for(let test of tests) output += _parseTest(test);

return output;
}

const _parseTest = function(test) {
let result = test.split(' ');
let out = `<DESCRIBE::>${result[1]}\n`;
out += result[3] != 'FAILED' ? `<PASSED::>Test Passed\n` : `<FAILED::>Test Failed\n`;
return out;
}
114 changes: 114 additions & 0 deletions test/runners/rust_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const expect = require('chai').expect;
const runner = require('../runner');

describe('rust runner', function() {
describe('.run', function() {
// Basic code run
it('should handle basic code evaluation', function(done) {
runner.run({
language: 'rust',
code: `
fn main() {
println!("Bam");
}
`
}, function(buffer) {
expect(buffer.stdout).to.equal('Bam\n');
done();
});
});

it('should handle invalid code', function(done) {
runner.run({
language: 'rust',
code: `
fn main() {
println!("Bam);
}
`
}, function(buffer) {
expect(buffer.stderr).to.contain('unterminated double quote');
done();
});
});

it('should allow mods inside setup', function(done) {
runner.run({
language: 'rust',
setup: `
use std::thread;
use std::sync::mpsc::channel;
`,
code: `
fn main() {
let (tx, rx) = channel();
thread::spawn(move|| {
tx.send(10).unwrap();
});
println!("{}", rx.recv().unwrap());
}
`
}, function(buffer) {
expect(buffer.stdout).to.contain(`10`);
done();
});
});

it('should handle basic tests', function(done) {
runner.run({
language: 'rust',
code: `
fn test_function() -> i32 {
return 69;
}
`,
fixture: `
#[test]
fn returns_number() {
assert_eq!(test_function(),69);
}
`,
testFramework: 'cargo'
}, function(buffer) {
expect(buffer.stdout).to.contain(`<DESCRIBE::>returns_number`);
expect(buffer.stdout).to.contain(`<PASSED::>Test Passed`);
done();
});
});

it('should handle tests with setup', function(done) {
runner.run({
language: 'rust',
setup: `
use std::thread;
use std::sync::mpsc::channel;
`,
code: `
fn async_thingo() {
let (tx, rx) = channel();
thread::spawn(move|| {
tx.send(10).unwrap();
});
}
fn doubler(n: i32) -> i32 {
return n * 2;
}
`,
fixture: `
#[test]
fn doubler_works() {
assert_eq!(doubler(2),4);
}
`,
testFramework: 'cargo'
}, function(buffer) {
expect(buffer.stdout).to.contain(`<DESCRIBE::>doubler_works`);
expect(buffer.stdout).to.contain(`<PASSED::>Test Passed`);
done();
});
});

});
});

0 comments on commit f20881e

Please sign in to comment.