|  | // Copyright (c) 2017, Compiler Explorer Authors | 
|  | // All rights reserved. | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright notice, | 
|  | //       this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above copyright | 
|  | //       notice, this list of conditions and the following disclaimer in the | 
|  | //       documentation and/or other materials provided with the distribution. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | 
|  | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|  | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
|  | // POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | import bodyParser from 'body-parser'; | 
|  | import express from 'express'; | 
|  |  | 
|  | import { CompileHandler, SetTestMode } from '../../lib/handlers/compile'; | 
|  | import { chai, makeCompilationEnvironment } from '../utils'; | 
|  |  | 
|  | SetTestMode(); | 
|  |  | 
|  | const languages = { | 
|  | a: {id: 'a'}, | 
|  | b: {id: 'b'}, | 
|  | d: {id: 'd'}, | 
|  | }; | 
|  |  | 
|  | describe('Compiler tests', () => { | 
|  | let app, compileHandler; | 
|  |  | 
|  | before(() => { | 
|  | const compilationEnvironment = makeCompilationEnvironment({languages}); | 
|  | compileHandler = new CompileHandler(compilationEnvironment); | 
|  |  | 
|  | const textParser = bodyParser.text({type: () => true}); | 
|  | const formParser = bodyParser.urlencoded({extended: false}); | 
|  |  | 
|  | app = express(); | 
|  | app.use(bodyParser.json()); | 
|  |  | 
|  | app.post('/noscript/compile', formParser, compileHandler.handle.bind(compileHandler)); | 
|  | app.post('/:compiler/compile', textParser, compileHandler.handle.bind(compileHandler)); | 
|  | }); | 
|  |  | 
|  | it('throws for unknown compilers', () => { | 
|  | return chai.request(app) | 
|  | .post('/NOT_A_COMPILER/compile') | 
|  | .then((res) => { | 
|  | res.should.have.status(404); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | describe('Noscript API', () => { | 
|  | it('supports compile', () => { | 
|  | return compileHandler.setCompilers([{ | 
|  | compilerType: 'fake-for-test', | 
|  | exe: 'fake', | 
|  | fakeResult: { | 
|  | code: 0, | 
|  | stdout: [{text: 'Something from stdout'}], | 
|  | stderr: [{text: 'Something from stderr'}], | 
|  | asm: [{text: 'ASMASMASM'}], | 
|  | }, | 
|  | }]).then(() => { | 
|  | return chai.request(app) | 
|  | .post('/noscript/compile') | 
|  | .set('Content-Type', 'application/x-www-form-urlencoded') | 
|  | .send('compiler=fake-for-test&source=I am a program') | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.text; | 
|  | res.text.should.contain('Something from stdout'); | 
|  | res.text.should.contain('Something from stderr'); | 
|  | res.text.should.contain('ASMASMASM'); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | describe('Curl API', () => { | 
|  | it('supports compile', () => { | 
|  | return compileHandler.setCompilers([{ | 
|  | compilerType: 'fake-for-test', | 
|  | exe: 'fake', | 
|  | fakeResult: { | 
|  | code: 0, | 
|  | stdout: [{text: 'Something from stdout'}], | 
|  | stderr: [{text: 'Something from stderr'}], | 
|  | asm: [{text: 'ASMASMASM'}], | 
|  | }, | 
|  | }]).then(() => { | 
|  | return chai.request(app) | 
|  | .post('/fake-for-test/compile') | 
|  | .set('Content-Type', 'application/x-www-form-urlencoded') | 
|  | .send('I am a program /* &compiler=NOT_A_COMPILER&source=etc */') | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.text; | 
|  | res.text.should.contain('Something from stdout'); | 
|  | res.text.should.contain('Something from stderr'); | 
|  | res.text.should.contain('ASMASMASM'); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | describe('JSON API', () => { | 
|  | it('handles text output', () => { | 
|  | return compileHandler.setCompilers([{ | 
|  | compilerType: 'fake-for-test', | 
|  | exe: 'fake', | 
|  | fakeResult: { | 
|  | code: 0, | 
|  | stdout: [{text: 'Something from stdout'}], | 
|  | stderr: [{text: 'Something from stderr'}], | 
|  | asm: [{text: 'ASMASMASM'}], | 
|  | }, | 
|  | }]).then(() => { | 
|  | return chai.request(app) | 
|  | .post('/fake-for-test/compile') | 
|  | .send({ | 
|  | options: '', | 
|  | source: 'I am a program', | 
|  | }) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.text; | 
|  | res.text.should.contain('Something from stdout'); | 
|  | res.text.should.contain('Something from stderr'); | 
|  | res.text.should.contain('ASMASMASM'); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | function makeFakeJson(source, options, fakeResult) { | 
|  | return compileHandler.setCompilers([{ | 
|  | compilerType: 'fake-for-test', | 
|  | exe: 'fake', | 
|  | fakeResult: fakeResult || {}, | 
|  | }]) | 
|  | .then(() => chai.request(app) | 
|  | .post('/fake-for-test/compile') | 
|  | .set('Accept', 'application/json') | 
|  | .send({ | 
|  | options: options || {}, | 
|  | source: source || '', | 
|  | })); | 
|  | } | 
|  |  | 
|  | it('handles JSON output', () => { | 
|  | return makeFakeJson('I am a program', {}, { | 
|  | code: 0, | 
|  | stdout: [{text: 'Something from stdout'}], | 
|  | stderr: [{text: 'Something from stderr'}], | 
|  | asm: [{text: 'ASMASMASM'}], | 
|  | }).then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.should.deep.equals({ | 
|  | asm: [{text: 'ASMASMASM'}], | 
|  | code: 0, | 
|  | input: { | 
|  | backendOptions: {}, | 
|  | filters: [], | 
|  | options: [], | 
|  | source: 'I am a program', | 
|  | }, | 
|  | stderr: [{text: 'Something from stderr'}], | 
|  | stdout: [{text: 'Something from stdout'}], | 
|  | }); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('parses options and filters', () => { | 
|  | return makeFakeJson('I am a program', { | 
|  | userArguments: '-O1 -monkey "badger badger"', | 
|  | filters: {a: true, b: true, c: true}, | 
|  | }) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.input.options.should.deep.equals(['-O1', '-monkey', 'badger badger']); | 
|  | res.body.input.filters.should.deep.equals({a: true, b: true, c: true}); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | describe('Query API', () => { | 
|  | function makeFakeQuery(source, query, fakeResult) { | 
|  | return compileHandler.setCompilers([{ | 
|  | compilerType: 'fake-for-test', | 
|  | exe: 'fake', | 
|  | fakeResult: fakeResult || {}, | 
|  | }]) | 
|  | .then(() => chai.request(app) | 
|  | .post('/fake-for-test/compile') | 
|  | .query(query || {}) | 
|  | .set('Accept', 'application/json') | 
|  | .send(source || '')); | 
|  | } | 
|  |  | 
|  | it('handles filters set directly', () => { | 
|  | return makeFakeQuery('source', {filters: 'a,b,c'}) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.input.options.should.deep.equals([]); | 
|  | res.body.input.filters.should.deep.equals({a: true, b: true, c: true}); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('handles filters added', () => { | 
|  | return makeFakeQuery('source', {filters: 'a', addFilters: 'e,f'}) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.input.options.should.deep.equals([]); | 
|  | res.body.input.filters.should.deep.equals({a: true, e: true, f: true}); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('handles filters removed', () => { | 
|  | return makeFakeQuery('source', {filters: 'a,b,c', removeFilters: 'b,c,d'}) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.input.options.should.deep.equals([]); | 
|  | res.body.input.filters.should.deep.equals({a: true}); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('handles filters added and removed', () => { | 
|  | return makeFakeQuery('source', {filters: 'a,b,c', addFilters: 'c,g,h', removeFilters: 'b,c,d,h'}) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.input.options.should.deep.equals([]); | 
|  | res.body.input.filters.should.deep.equals({a: true, g: true}); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | describe('Multi language', () => { | 
|  | function makeFakeJson(compiler, lang) { | 
|  | return compileHandler.setCompilers([ | 
|  | { | 
|  | compilerType: 'fake-for-test', | 
|  | id: 'a', | 
|  | lang: 'a', | 
|  | exe: 'fake', | 
|  | fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG A'}]}, | 
|  | }, | 
|  | { | 
|  | compilerType: 'fake-for-test', | 
|  | id: 'b', | 
|  | lang: 'b', | 
|  | exe: 'fake', | 
|  | fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B'}]}, | 
|  | }, | 
|  | { | 
|  | compilerType: 'fake-for-test', | 
|  | id: 'a', | 
|  | lang: 'b', | 
|  | exe: 'fake', | 
|  | fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B but A'}]}, | 
|  | }, | 
|  | ]) | 
|  | .then(() => chai.request(app) | 
|  | .post(`/${compiler}/compile`) | 
|  | .set('Accept', 'application/json') | 
|  | .send({ | 
|  | lang: lang, | 
|  | options: {}, | 
|  | source: '', | 
|  | })); | 
|  | } | 
|  |  | 
|  | it('finds without language', () => { | 
|  | return makeFakeJson('b', {}) | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.asm.should.deep.equals([{text: 'LANG B'}]); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('disambiguates by language, choosing A', () => { | 
|  | return makeFakeJson('a', 'a') | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.asm.should.deep.equals([{text: 'LANG A'}]); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  |  | 
|  | it('disambiguates by language, choosing B', () => { | 
|  | return makeFakeJson('a', 'b') | 
|  | .then(res => { | 
|  | res.should.have.status(200); | 
|  | res.should.be.json; | 
|  | res.body.asm.should.deep.equals([{text: 'LANG B but A'}]); | 
|  | }) | 
|  | .catch(err => { | 
|  | throw err; | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | }); |