Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI Controller #60

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
444e0de
Implement initial argument parser.
yashlala Jan 22, 2021
c56d5d4
Fix: incorrect file layout introduced by git rebase.
yashlala Jan 26, 2021
fef2035
Add npm packages `yargs` and `@types/yargs`.
yashlala Jan 26, 2021
b12b510
Remove duplicate `tsconfig.json`.
yashlala Jan 26, 2021
152bae9
Update parser formatting/import styles for tests.
yashlala Jan 26, 2021
5dfa22f
Remove parser comments regarding solved bug.
yashlala Jan 26, 2021
4e19ecc
Add dependency-tracking recursive make script.
yashlala Jan 28, 2021
aa80084
Merge remote-tracking branch 'refs/remotes/origin/master'
yashlala Feb 2, 2021
d10682d
Merge branch 'master' into initiate-job.
yashlala Feb 2, 2021
96c36df
Make parser into a proper typescript module.
yashlala Feb 4, 2021
f61863b
Begin tracking recursive Makefile snippet.
yashlala Feb 13, 2021
407e051
Merge branch 'master' into initiate-job
Rohanator9000 Feb 19, 2021
b4f321e
eslint casing
Rohanator9000 Feb 19, 2021
0a9bb2b
makefile extracting working, but trash code
Rohanator9000 Feb 22, 2021
6d92ce2
see . . . trash code
Rohanator9000 Feb 22, 2021
d2a1626
Move all Controller-related files to their own dir.
yashlala Feb 23, 2021
40bcbf0
Remove prototype recursive makefile targets.
yashlala Feb 23, 2021
c65008b
Add "target" field to CLI controller.
yashlala Feb 23, 2021
9916b5e
Rename "Parser" code to CommandLineController.
yashlala Feb 23, 2021
9fe9fd3
fixed errors + formatting
Rohanator9000 Feb 23, 2021
55d7f04
fixed package.json and lock
Rohanator9000 Feb 23, 2021
8cb0f1f
Merge branch 'master' into initiate-job
Rohanator9000 Feb 23, 2021
676e975
Reject invalid CLI options via yargs.strict().
yashlala Feb 26, 2021
1f8b00f
Add tests for Command Line Controller.
yashlala Feb 26, 2021
fe5345f
Remove all Makefile trace parsing code.
yashlala Feb 26, 2021
c9ccde1
Merge branch 'master' into initiate-job
yashlala Feb 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"jest/consistent-test-it": "error",
"jest/prefer-spy-on": "error",
"jest/prefer-todo": "error",
"jest/require-top-level-describe": "error"
"jest/require-top-level-describe": "error",
"prefer-destructuring": ["error", { "array": false }]
},
"env": {
"node": true,
Expand Down
6 changes: 3 additions & 3 deletions bin/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ function clientDone() {

const job3: Job = new NormalJob('third')
const job4: Job = new NormalJob('fourth')
const job2: Job = new NormalJob('second', new Set([job3]))
const job1: Job = new NormalJob('first', new Set([job4, job2]))
const job5: Job = new NormalJob('fifth', new Set([job1]))
const job2: Job = new NormalJob('second', [], new Set([job3]))
const job1: Job = new NormalJob('first', [], new Set([job4, job2]))
const job5: Job = new NormalJob('fifth', [], new Set([job1]))

const client: Client = new Http2Client(new HeapJobOrderer([job5]))
client.on('progress', console.log)
Expand Down
8 changes: 7 additions & 1 deletion package-lock.json

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

2 changes: 1 addition & 1 deletion src/Client/Http2Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class Http2Client extends EventEmitter implements Client {
*/
private assignJobToDaemon(job: Job, daemon: ClientHttp2Session) {
this.availableDaemons.delete(daemon)
const request = daemon.request({ ':path': `/${job.getName()}` })
const request = daemon.request({ ':path': `/${job.getTarget()}` })
daemon.on('error', () => this.jobOrderer.reportFailedJob(job))

let data = ''
Expand Down
49 changes: 49 additions & 0 deletions src/Controller/CommandLineController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as yargs from 'yargs'

export interface JunknetArguments {
makefile: string
docker_image: string
target: string | null
}

/**
* Interpret a list of arguments, returning the relevant ones in the f
*
* @param argv - the invoking process's command-line arguments.
* @returns JunknetInterface object containing option values.
*/
export function interpretArgv(argv: readonly string[]): JunknetArguments {
// Use only the first two elements; Node.js appends extra elements to process.argv.
const yargsArgv = yargs(argv.slice(2))
.options({
f: {
alias: 'makefile',
type: 'string',
default: 'Makefile',
desc: 'The Makefile to process',
},
i: {
alias: 'docker-image',
type: 'string',
default: 'ubuntu:18.04',
desc: 'The Docker Image to run',
},
t: {
alias: 'target',
type: 'string',
desc: 'The Makefile target to build',
},
})
.strict().argv

let target = null
if (yargsArgv.t !== undefined) {
target = yargsArgv.t
}

return {
makefile: yargsArgv.f,
docker_image: yargsArgv.i,
target,
}
}
14 changes: 9 additions & 5 deletions src/Job/Job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
*/
export interface Job {
/**
* Get this job's name.
* Get this job's target.
*
* This method may be removed in the future because Jobs may no longer have names.
* The target is the resulting file.
*
* @experimental
* @returns This job's name.
* @returns This target.
*/
getName(): string
getTarget(): string

/**
* Gets this Job's prerequisites as an iterable.
Expand All @@ -27,4 +26,9 @@ export interface Job {
* @returns The number of prerequisites.
*/
getNumPrerequisites(): number

/**
* Returns the commands to run (in-order and synchronously) that will result.
*/
getCommands(): string[]
}
19 changes: 12 additions & 7 deletions src/Job/NormalJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ export class NormalJob implements Job {
private prerequisites: Set<Job>

/**
* @param name - The job's name. Must be unique between jobs in the same dependency graph.
* @param target - The job's name. Must be unique between jobs in the same dependency graph.
* @param prerequisites - An optional set containing all of this job's prerequisites. Defaults to no prerequisites.
*/
constructor(
private readonly name: string,
private readonly target: string,
private readonly commands: string[] = [],
prerequisites: Set<Job> = new Set(),
) {
this.prerequisites = new Set(prerequisites) // Make a copy so the caller can't directly access prerequisites. Encapsulation!
Expand All @@ -20,8 +21,8 @@ export class NormalJob implements Job {
/**
* @returns The job's name.
*/
public getName(): string {
return this.name
public getTarget(): string {
return this.target
}

/**
Expand All @@ -47,11 +48,15 @@ export class NormalJob implements Job {
*/
public toString(): string {
if (this.prerequisites.size === 0) {
return `Source job ${this.name}.`
return `Source job ${this.target}.`
}

return `Job "${this.name}" depending on ${Array.from(this.prerequisites)
.map((prerequisite) => prerequisite.getName())
return `Job "${this.target}" depending on ${Array.from(this.prerequisites)
.map((prerequisite) => prerequisite.getTarget())
.join(', ')}.`
}

public getCommands(): string[] {
return this.commands
}
}
77 changes: 77 additions & 0 deletions test/unit/CommandLineController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Tests for the Command-Line Argument Parser
*/

// When yargs encounters an invalid option, it'll automatically invoke process.exit().
// This is a problem, because it would muck up our testing framework.
// We could handle this by explicitly passing a fail() callback to yargs,
// but this would override the very-nice default handler. In short, testing
// this scenario would require a lot of code.
//
// I don't think that the effort is worth it, because our code is just a
// wrapper around yargs anyways (I trust their tests).

import * as cli from '../../src/Controller/CommandLineController'

describe('Parser', () => {
it('interprets short options', () => {
const argv: readonly string[] = [
'0',
'1',
'2',
'-f',
'mf',
'-i',
'di',
'-t',
't',
]
const results = cli.interpretArgv(argv)
expect(results.makefile).toBe('mf')
expect(results.docker_image).toBe('di')
expect(results.target).toBe('t')
})

it('interprets --option=var style long options', () => {
const argv: readonly string[] = [
'0',
'1',
'2',
'3',
'--makefile=mf',
'--docker-image=di',
'--target=t',
]
const results = cli.interpretArgv(argv)
expect(results.makefile).toBe('mf')
expect(results.docker_image).toBe('di')
expect(results.target).toBe('t')
})

it('interprets "--option var" style long options', () => {
const argv: readonly string[] = [
'0',
'1',
'2',
'3',
'--makefile',
'mf',
'--docker-image',
'di',
'--target',
't',
]
const results = cli.interpretArgv(argv)
expect(results.makefile).toBe('mf')
expect(results.docker_image).toBe('di')
expect(results.target).toBe('t')
})

it('sets correct default options', () => {
const argv: readonly string[] = ['0', '1']
const results = cli.interpretArgv(argv)
expect(results.makefile).toBe('Makefile')
expect(results.docker_image).toBe('ubuntu:18.04')
expect(results.target).toBeNull()
})
})
10 changes: 8 additions & 2 deletions test/unit/HeapJobOrderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ describe('HeapJobOrderer', () => {
const sourceJob: Job = new NormalJob('sourceJob')
const intermediateJob1: Job = new NormalJob(
'intermediateJob1',
[''],
new Set([sourceJob]),
)
const intermediateJob2: Job = new NormalJob(
'intermediateJob2',
[''],
new Set([sourceJob, intermediateJob1]),
)
const rootJob1: Job = new NormalJob('rootJob1', new Set([sourceJob]))
const rootJob2: Job = new NormalJob('rootJob2', new Set([intermediateJob2]))
const rootJob1: Job = new NormalJob('rootJob1', [''], new Set([sourceJob]))
const rootJob2: Job = new NormalJob(
'rootJob2',
[''],
new Set([intermediateJob2]),
)

const rootJobs: Job[] = [rootJob1, rootJob2]
const jobOrderer = new HeapJobOrderer(rootJobs)
Expand Down
9 changes: 5 additions & 4 deletions test/unit/NormalJob.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ describe('NormalJob', () => {
it('returns the correct name', () => {
const expectedName = 'jobName'
const job = new NormalJob(expectedName)
expect(job.getName()).toEqual(expectedName)
expect(job.getTarget()).toEqual(expectedName)
})

it('returns the correct number of prerequisites for sources', () => {
const sourceJob = new NormalJob('', new Set([]))
const sourceJob = new NormalJob('')
expect(sourceJob.getNumPrerequisites()).toEqual(0)
})

it('returns the correct number of prerequisites for nonsources', () => {
const nonsourceJob = new NormalJob(
'',
[''],
new Set([new NormalJob(''), new NormalJob(''), new NormalJob('')]),
)
expect(nonsourceJob.getNumPrerequisites()).toEqual(3)
Expand All @@ -25,7 +26,7 @@ describe('NormalJob', () => {
const toDelete = new NormalJob('asd')
const prerequisites = new Set([toDelete])

const job = new NormalJob('job', prerequisites)
const job = new NormalJob('job', [''], prerequisites)
prerequisites.delete(toDelete)
expect(job.getNumPrerequisites()).toEqual(1)
})
Expand All @@ -36,7 +37,7 @@ describe('NormalJob', () => {
new NormalJob('2'),
new NormalJob('3'),
])
const job = new NormalJob('job', prerequisites)
const job = new NormalJob('job', [''], prerequisites)

expect(new Set(job.getPrerequisitesIterable())).toEqual(prerequisites)
})
Expand Down