SWE 432, Fall 2018, Homework 1, Due Sept 17, 10 am
Please post questions on Piazza
Updates:
- 8/31: Renamed “addMemeConfiguration” to “addMemeTemplate” — it is expected to process meme templates, not configurations.
- 9/5: Added hint for part 1
- 9/7: Clarified some of the test specifications
- 9/12: Added example for addMemeTemplate
- 9/13: Added link to Jest’s promise error handling docs
Project Overview
This semester, while you learn the key concepts of web application development, you will apply these skills to build a meme generator, which we will refer to as the Memebase. A meme is a cultural phenomenon that combines some popular graphic (for example, a cat or TV/movie character) with large-block text. As an example, here is a meme generated by combining a graphic called “news cat” with some text relevant to this project:
You will build the Memebase in four different components; each assignment will be one component. Each component will conform to a common interface, allowing them to be independently developed and tested. The final result will be a working website that allows users to generate memes, create accounts to share and track their memes.
This Assignment
For the first component of the Memebase (this assignment), you will build a simple JavaScript (nodejs) application that takes as input a JSON file of desired memes and generates as output the finished graphics by overlaying text on those graphics. This tool will run on the command line.
Getting Started
You’ll need to install Node.js on your computer — version 8 or higher. You can follow the instructions on the Node.js installation page for the “LTS” version to get it installed.
You’ll also need to install GraphicsMagick on your computer. Windows users can use the GraphicsMagick installer package. Mac users can install homebrew, then run brew install graphicsmagick in the terminal. Linux users should use whatever package manager they use to install graphicsmagick.
Note – After installing Node and GraphicsMagick, to make sure it works in your command shell, you should close your shell and open a fresh one.
Finally, it is not required that you use an IDE, but it’s a really really really really good idea. JetBrains’ IntelliJ/WebStorm is a great choice — if you already have IntelliJ you can add the NodeJS component to it, or simply download WebStorm. You can get a free license to use any of the JetBrains IDEs by filling out this simple form.
Then, you are ready to get started. Download the handout, extract it, and then open a terminal in the handout directory. Run the command npm install, which will download all of the JavaScript modules that the assignment will use.
The handout contains a few stubbed out JavaScript files:
MemeGenerator.js – An (empty) class file that you’ll implement the meme generator in by implementing the following methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
class MemeGenerator {
/**
* Creates a new meme generator, parsing the meme config file in
*/
constructor(configFile) {
}
/**
* Adds a new meme template to the supported set of memes
* @param memeTemplate a new meme, as a JS object in the same format as the meme data file
*/
addMemeTemplate(memeTemplate) {
}
/**
* Checks that a request to generate a meme is valid, specifically:
* That the meme referenced is defined
* That the text blocks defined in the requested meme are all valid (exist) for that meme referenced
* That the referenced graphic file exists
* @param meme meme object to check
* @returns A string if there is an error, false if there is no error
*/
validateMeme(meme) {
}
/**
* Given a meme configuration, generates the composite graphic (layering the text atop of the graphic)
* A meme configuration references a "base meme" template --- the template defines where text will be positioned
* and its font/stroke/color; the meme configuration specifies which template to use and what text to put in the
* different positions. The meme is written directly out to the image file specified in the configuration.
*
* Note that this function returns a PROMISE representing the generation of the image, as generating the image is an
* async operation.
*
* @param meme configuration for the meme to generate
* @returns Promise that is completed when the image is successfully written out to disk
*/
generateMeme(meme) {
}
}
|
index.js – a very simple driver that demonstrates the usage of the meme generator
MemeGenerator.tests.js – Automated tests for the MemeGenerator
As well as a few data files:
meme-data.json – A JSON file describing the different meme templates that your meme generator will support. A meme template consists of a reference to a graphic, and a description of the various text areas and formats that the graphic supports. For instance, here is the description of the Gru meme template:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
"Gru": {
"graphic": "resources/Gru.jpg",
"text": {
"step1": {
"font": "resources/Verdana.ttf",
"size": 12,
"stroke": 0,
"fillColor": "#000000",
"strokeColor": "#FFFFFF",
"location": {
"x": 175,
"y": 60,
"gravity": null
}
},
"step2": {
"font": "resources/Verdana.ttf",
"size": 12,
"stroke": 0,
"fillColor": "#000000",
"strokeColor": "#FFFFFF",
"location": {
"x": 475,
"y": 60,
"gravity": null
}
},
"step3": {
"font": "resources/Verdana.ttf",
"size": 12,
"stroke": 0,
"fillColor": "#000000",
"strokeColor": "#FFFFFF",
"location": {
"x": 175,
"y": 250,
"gravity": null
}
},
"step4": {
"font": "resources/Verdana.ttf",
"size": 12,
"stroke": 0,
"fillColor": "#000000",
"strokeColor": "#FFFFFF",
"location": {
"x": 475,
"y": 250,
"gravity": null
}
}
|
Each text block is a stand-in for text that can be placed in by the meme builder. The location is specified in x/y pixel coordinates, and/or using GraphicsMagick’s gravity parameter.
The template file also defines default text positioning (for 1 line centered at the top and 1 line centered at the bottom), making it easy to add many meme templates that use that same standard text formatting, for example:
1
2
3
|
"NewsCat": {
"graphic": "resources/newscat.jpg"
}
|
Since there is no text element defined here, the meme generator will inherit the element from the default template.
sample-memes.json – Defines some meme configurations that you can use to test your meme generator. Example:
1
2
3
4
5
6
7
8
|
{
"name": "You-Get-a-Meme.png",
"meme": "Oprah",
"text":{
"top":"YOU GET A MEME!\nYOU GET A MEME!",
"bottom":"EVERYONE GETS A MEME!"
}
}
|
This configuration defines a meme that will get output to a file named You-Get-A-Meme.png, using the meme template named Oprah, and defining text for both the top and bottom position (which are defined in the template). Note that if you would like a line break to appear in the text, use the \n escape character. If you would like quotes, they also need to be escaped (e.g. ” becomes \”).
The assignment is broken up into three components:
Part 1 (30 points): Reading and Checking JSON files
The first step will be reading in the list of meme templates and configurations and validating them. You should implement the functions constructor, addMemeTemplate and validateMeme in MemeGenerator.js. You should partially implement generateMeme, stubbing it to simply call validateMeme (no need to generate the graphic yet).
When the constructor is called, it will be passed the name of a JSON file with the same structure as meme-data.json. You should parse the JSON file in and store the information about each meme template as an instance field of the MemeGenerator. You should implement addMemeTemplate to take a single meme template as input and add it to that same internal data-structure (for later use).
Added 9/5: Hints – You should check out the built-in function JSON.parse for converting JSON into a JS object; you should check out fs.readFileSync to see how to read the contents of a file into a string (which perhaps then you might parse into a JS object).
Added 9/12: Hint – Here is an example object that might be passed to addMemeTemplate:
g.addMemeTemplate({ "Example": { "graphic": "resources/example.jpg" } });
When
generateMeme is called, you’ll pass the meme configuration on to
validateMeme, which will do some sanity checking on both the meme configuration and the meme template. Specifically, it will check for:
- If the requested meme template exists (aka, check
meme.meme). If not,
validateMeme should return the error message
1Error: meme '{{meme template name}}' does not exist - If the meme configuration references text fields that don’t exist for that meme template (
meme.text), it should return the error message:
1Error: unexpected text block - If the graphic file defined by the meme template (
meme.graphics) doesn’t exist, it should return the message
1Error: graphic file '{{name of file}}' does not exist
Part 1 will be graded with 4 automated tests (6 points each), plus 6 points from manual inspection. These automated tests are not included with the handout, but will run when you submit on Autolab (see part 2).
Part 2 (20 points): Writing Your Own Tests
We have written four automated tests for part 1 to check the error handling. Before moving on to actually generating graphics, you will implement the same tests yourselves. This will help you to get your feet with with writing tests for JS, and also with using asynchronous Promises.
Implement the following four tests:
1 |
test('Meme with invalid text generates an exception',...)
|
Note (9/7): The above test should be specifying text for a meme, specifying a text block that doesn’t exist.
1 |
test('Invalid meme name generates an exception'...)
|
Note (9/7): The above test should be checking the “name” field of a meme configuration
1 |
test('Meme with invalid text (from the default template) generates an exception'...)
|
Note (9/7): The above test should be specifying a “top” or “bottom” text for a meme that uses a meme template that has “text” blocks defined, but does not have “top” or “bottom” as valid options.
1 |
test('Missing meme image generates an exception'...)
|
You can consult the Jest API for pointers on how to structure these tests. The logic of these tests should be self explanatory based on the title of the test, if in doubt, post on Piazza.
Hint: look carefully at the doc example to test promises returning errors – in particular, the use of catch, and that return.
The tests must interact with the MemeGenerator only with the methods constructor, addMemeTemplate, and generateMeme — they must not call validateMeme directly, and must not access any fields of your MemeGenerator or call any other methods of it. Beware that generateMeme returns a promise — you must write your tests to await the completion of the call.
Part 2 will be graded using known correct and incorrect implementations of the MemeGenerator class — your tests should pass on the correct implementation and fail on the wrong implementations. You will not have access to our correct or incorrect implementations of the MemeGenerator — but each time that you submit on AutoLab you’ll get the results. For each of the four tests, you will receive up to 5 points. You’ll get 4 points if your test passes and fails when it should, plus 1 point from manual inspection. A test that passes always or fails always will receive 0 points.
Part 2 (50 points): Generating Graphics
Finally! Once you have all of the data loaded in, we get to the fun part – generating the graphics. You will do this in the generateMeme function using the gm package (the steps above handled the installation so you should be good to go). Given a meme configuration and its corresponding template, you will:
- Read the template image into NodeJS using the gm() function, and resize it to 600px width
- For each text block that is defined by the template (and configured by the meme configuration), you will:
- Set the stroke width and stroke color (if it’s not 0)
- Set the text font size and font
- Set the fill color
- Draw the requested text, using the location (x, y, and gravity) defined by the template and text string defined by the configuration
- Add the text “SWE 432” without quotes in the bottom left of the image, specifically:
- Using font Impact (included in the project, easily referenced as "./resources/Impact.ttf"), size 14
- Using a black stroke of size 1 and a white fill
- Draw the text “SWE 432” at the x,y offset of 5,5 from the gravity location “SouthWest”
- Write the image out to disk at the specified location (
name in the meme configuration). WARNING: the “write” function in gm is asynchronous — so you will need to use a callback to find out when the image has been written out.
- If this succeeds, resolve the promise
- If this fails, reject the promise, passing the error
To successfully pass the tests, you will need to perform these steps precisely. In particular, you must make sure to only resolve the promise after writing the image to disk succeeds.
Hint: the following functions from gm are probably useful to look at;
- font
- resize
- stroke
- fill
- drawText
- write
You can run your meme generator for testing purposes on a few inputs by running the index.js file (e.g. node index.js), which will attempt to generate a variety of memes.
You can automatically check your generated files for conformance by running
npm test. When you run
npm test, the generated images are checked against our reference memes (in the
reference-images directory); any discrepancies are reported graphically. For instance, here is the debugging output from an incorrect implementation of the meme generator that does not add the “SWE 432”:
Most of the image is spot-on: this is the part that is faded out. The red/yellow in the bottom left is indicating that that part of the image is incorrect.
Part 3 will be graded primarily by the automated test suite (which is available to you both locally with npm test and also runs when you submit to AutoLab). There are 4 tests, worth 10 points each; the remaining 10 points will be assigned from manual code review.
Grading
Your meme generator will be graded for correctness using a series of automated tests, and also by hand (as described in each section above). When you submit to Autolab, it will assume that you receive full marks from the manual grading component (6 points for part 1, 5 points for part 2 and 10 points for part 3); this may of course go down when we actually grade your final submission. We are primarily looking for basic code quality (naming, indentation, etc), as well as correctness with handling asynchronous operations (which are incredibly tricky to check automatically).
Hand In Instructions
You must turn in your assignment using Autolab (You MUST be on the campus network, or connected to the GMU VPN to connect to Autolab). If you did not receive a confirmation email from Autolab to set a password, enter your @gmu.edu (NOT @masonlive) email, and click “forgot password” to get a new password.
To prepare your code to be submitted, run npm pack in the assignment directory. This will create an archive thememebase-1.0.0.tgz – this is what you will submit to Autolab. Do not try and generate this archive without using npm; it’s highly likely to cause Autolab to reject it. When you upload your assignment, Autolab will automatically compile and test it. You should verify that the result that Autolab generates is what you expect. Your code is built and tested in a Linux VM. Assignments that do not compile using our build environment will receive a maximum of 50%. Note that we have provided ample resources for you to verify that our view of your assignment is the same as your own: you will see the result of the compilation and test execution for your assignment when you submit it.
You can resubmit your assignment an unlimited number of times before the deadline. Note the course late-submission policy: assignments will be accepted up until 24 hours past the deadline at a penalty of 10%; after 24 hours, no late assignments will be accepted, no exceptions.
Questions
Please post questions on Piazza