Skip to content

Commit

Permalink
Common function, error handling & unit tests for repl/test
Browse files Browse the repository at this point in the history
  • Loading branch information
fdodino committed Nov 5, 2023
1 parent 5e9fa6f commit 1959236
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 71 deletions.
7 changes: 7 additions & 0 deletions examples/bad-files-examples/fileWithParseErrors.wlk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object pepita {
var energia = 0
var extra = 0

// method without parentheses does not parse ok
method energiaTotal = energia + extra
}
5 changes: 5 additions & 0 deletions examples/bad-files-examples/fileWithValidationErrors.wlk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class A {
method go() {
self = 1
}
}
72 changes: 72 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@
},
"devDependencies": {
"@types/chai": "^4.3.9",
"@types/chai-as-promised": "^7.1.7",
"@types/cytoscape": "^3.19.7",
"@types/express": "^4.17.20",
"@types/mocha": "^10.0.3",
"@types/node": "^18.14.5",
"@types/p5": "^1.7.1",
"@types/sinon": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"eslint": "^8.52.0",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
Expand Down
14 changes: 4 additions & 10 deletions src/commands/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import link from 'wollok-ts/dist/linker'
import { ParseError } from 'wollok-ts/dist/parser'
import natives from 'wollok-ts/dist/wre/wre.natives'
import { getDataDiagram } from '../services/diagram-generator'
import { buildEnvironmentForProject, failureDescription, getFQN, linkSentence, publicPath, successDescription, valueDescription, validateEnvironment } from '../utils'
import { buildEnvironmentForProject, failureDescription, getFQN, linkSentence, publicPath, successDescription, valueDescription, validateEnvironment, handleError } from '../utils'

export const REPL = 'REPL'

Expand Down Expand Up @@ -94,17 +94,11 @@ export async function initializeInterpreter(autoImportPath: string | undefined,
const replPackage = new Package({ name: REPL })
environment = link([replPackage], environment)
}
return new Interpreter(Evaluation.build(environment, natives))
} catch (error: any) {
if (error.level === 'error') {
logger.error(failureDescription('Exiting REPL due to validation errors!'))
} else {
logger.error(failureDescription('Uh-oh... Unexpected Error!'))
logger.debug(failureDescription('Stack trace:', error))
}
process.exit()
handleError(error)
return process.exit(1)

Check warning on line 100 in src/commands/repl.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/repl.ts#L99-L100

Added lines #L99 - L100 were not covered by tests
}

return new Interpreter(Evaluation.build(environment, natives))
}

// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Expand Down
108 changes: 57 additions & 51 deletions src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Entity, Node, Test } from 'wollok-ts'
import { is, match, when } from 'wollok-ts/dist/extensions'
import interpret from 'wollok-ts/dist/interpreter/interpreter'
import natives from 'wollok-ts/dist/wre/wre.natives'
import { buildEnvironmentForProject, failureDescription, successDescription, valueDescription, validateEnvironment } from '../utils'
import { buildEnvironmentForProject, failureDescription, successDescription, valueDescription, validateEnvironment, handleError } from '../utils'

const { log } = console

Expand All @@ -15,67 +15,73 @@ type Options = {
}

export default async function (filter: string | undefined, { project, skipValidations }: Options): Promise<void> {
logger.info(`Running all tests ${filter ? `matching ${valueDescription(filter)} ` : ''}on ${valueDescription(project)}`)
try {

const environment = await buildEnvironmentForProject(project)
logger.info(`Running all tests ${filter ? `matching ${valueDescription(filter)} ` : ''}on ${valueDescription(project)}`)

validateEnvironment(environment, skipValidations)
const environment = await buildEnvironmentForProject(project)

const filterTest = filter?.replaceAll('"', '') ?? ''
const possibleTargets = environment.descendants.filter(is(Test))
const onlyTarget = possibleTargets.find(test => test.isOnly)
const testMatches = (filter: string) => (test: Test) => !filter || test.fullyQualifiedName.replaceAll('"', '').includes(filterTest)
const targets = onlyTarget ? [onlyTarget] : possibleTargets.filter(testMatches(filterTest))
validateEnvironment(environment, skipValidations)

logger.info(`Running ${targets.length} tests...`)
const filterTest = filter?.replaceAll('"', '') ?? ''
const possibleTargets = environment.descendants.filter(is(Test))
const onlyTarget = possibleTargets.find(test => test.isOnly)
const testMatches = (filter: string) => (test: Test) => !filter || test.fullyQualifiedName.replaceAll('"', '').includes(filterTest)
const targets = onlyTarget ? [onlyTarget] : possibleTargets.filter(testMatches(filterTest))

const debug = logger.getLevel() <= logger.levels.DEBUG
if (debug) time('Run finished')
const interpreter = interpret(environment, natives)
const failures: [Test, Error][] = []
let successes = 0
logger.info(`Running ${targets.length} tests...`)

environment.forEach(node => match(node)(
when(Test)(node => {
if (targets.includes(node)) {
const debug = logger.getLevel() <= logger.levels.DEBUG
if (debug) time('Run finished')
const interpreter = interpret(environment, natives)
const failures: [Test, Error][] = []
let successes = 0

environment.forEach(node => match(node)(
when(Test)(node => {
if (targets.includes(node)) {
const tabulation = ' '.repeat(node.fullyQualifiedName.split('.').length - 1)
try {
interpreter.fork().exec(node)
logger.info(tabulation, successDescription(node.name))
successes++
} catch (error: any) {
logger.info(tabulation, failureDescription(node.name))
failures.push([node, error])
}
}
}),

when(Entity)(node => {
const tabulation = ' '.repeat(node.fullyQualifiedName.split('.').length - 1)
try {
interpreter.fork().exec(node)
logger.info(tabulation, successDescription(node.name))
successes++
} catch (error: any) {
logger.info(tabulation, failureDescription(node.name))
failures.push([node, error])
if(targets.some(target => node.descendants.includes(target))){
logger.info(tabulation, node.name)
}
}
}),
}),

when(Entity)(node => {
const tabulation = ' '.repeat(node.fullyQualifiedName.split('.').length - 1)
if(targets.some(target => node.descendants.includes(target))){
logger.info(tabulation, node.name)
}
}),
when(Node)( _ => { }),
))

when(Node)( _ => { }),
))
log()
if (debug) timeEnd('Run finished')

log()
if (debug) timeEnd('Run finished')
failures.forEach(([test, error]) => {
log()
logger.error(failureDescription(bold(test.fullyQualifiedName), error))
})

failures.forEach(([test, error]) => {
log()
logger.error(failureDescription(bold(test.fullyQualifiedName), error))
})

logger.info(
'\n',
successDescription(`${successes} passing`),
failures.length ? failureDescription(`${failures.length} failing`) : '',
'\n'
)

if (failures.length) {
process.exit(2)
logger.info(
'\n',
successDescription(`${successes} passing`),
failures.length ? failureDescription(`${failures.length} failing`) : '',
'\n'
)

if (failures.length) {
process.exit(2)
}
} catch (error: any) {
handleError(error)
return process.exit(1)
}
}
20 changes: 10 additions & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { blue, bold, green, italic, red, yellowBright } from 'chalk'
import { blue, bold, green, italic, red, redBright, yellowBright } from 'chalk'
import fs, { Dirent } from 'fs'
import { readFile } from 'fs/promises'
import globby from 'globby'
Expand Down Expand Up @@ -44,11 +44,6 @@ export async function buildEnvironmentForProject(project: string, files: string[

if (debug) time('Building environment')
try { return buildEnvironment(environmentFiles) }
catch (error: any) {
logger.error(failureDescription(`Fatal error while building the environment. ${error.message}`))
logger.debug(error)
return process.exit(10)
}
finally { if (debug) timeEnd('Building environment') }
}

Expand All @@ -61,16 +56,21 @@ export const validateEnvironment = (environment: Environment, skipValidations: b
logger.info(successDescription('No problems found building the environment!'))
}
else if(problems.some(_ => _.level === 'error')) {
logger.error(failureDescription('Aborting run due to validation errors!'))
process.exit(1)
throw new Error('Aborting run due to validation errors!')
}
} catch (error: any) {
logger.error(failureDescription(`Fatal error while building the environment. ${error.message}`))
logger.debug(error)
process.exit(2)
throw new Error(`Fatal error while building the environment. ${error.message}`)
}
}
}

export const handleError = (error: any): void => {
logger.error(redBright('πŸ’₯ Uh-oh... Unexpected Error!'))
logger.error(redBright(error.message))
logger.debug(failureDescription('ℹ️ Stack trace:', error))

Check warning on line 71 in src/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/utils.ts#L69-L71

Added lines #L69 - L71 were not covered by tests
}

// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
// PRINTING
// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Expand Down
26 changes: 26 additions & 0 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { join } from 'path'
import { buildEnvironmentForProject, validateEnvironment } from '../src/utils'
import chaiAsPromised from 'chai-as-promised'
import chai from 'chai'

describe('build & validating environment', () => {

const badProjectPath = join('examples', 'bad-files-examples')

it('should throw an exception if parsing fails', async () => {
chai.use(chaiAsPromised)
const expect = chai.expect
await expect(buildEnvironmentForProject(badProjectPath, ['fileWithParseErrors.wlk'])).to.eventually.be.rejectedWith(/Failed to parse fileWithParseErrors.wlk/)
})

it('should throw an exception if validation fails', async () => {
const environment = await buildEnvironmentForProject(badProjectPath, ['fileWithValidationErrors.wlk'])
chai.expect(() => { validateEnvironment(environment, false) }).to.throw(/Fatal error while building the environment/)
})

it('should not throw an exception if validation fails but you want to skip validation', async () => {
const environment = await buildEnvironmentForProject(badProjectPath, ['fileWithValidationErrors.wlk'])
chai.expect(() => { validateEnvironment(environment, true) }).to.not.throw()
})

})

0 comments on commit 1959236

Please sign in to comment.