CSC 230 Project 4

Temp Agency

For this project, you will write a program that manages a temp agency that maintains a database of employees. Companies contract with the agency to hire employees on a temporary basis. At start-up, the program will read one or more files containing employees and store them in the agency database. It will then let the user enter commands to view the employees and to assign them to various companies.

The following shows an execution of the program. The text that's in bold shows what the user types. The rest is output from the program. Here, the user asks the program to read employees from two files, list-b.txt and list-c.txt. The "cmd> " at the start of some lines is a prompt for the user.

The user first asks for a list of all the employees. The output lists employees from the input files, one per line. Each employee is listed with an ID number on the left, a first and last name, their skill, and the company to which they are assigned or "Available" if they are not currently assigned to a company. The user then asks for a list of just the employees that have the skill, Sales. Afterwards, the user assigns several employees to the company, MaxRealty, and then lists all employees who are assigned to that company. Next, the usre removes one of the employees assigned to MaxRealty and re-lists the employees assigned to the company to show this person was removed. Finally, the user then quits the program.

$ ./agency list-b.txt list-c.txt
cmd> list
list
ID   First Name      Last Name       Skill           Assignment
0165 Phillip         Sanders         Accounting      Available           
0337 Timothy         Donnelly        Accounting      Available           
1234 Jessica         Strong          IT              Available           
1945 Maria           Gilligan        Administration  Available           
2334 James           Bush            Sales           Available           
2870 Bonnie          Jones           Sales           Available           
3567 Don             Hernandez       IT              Available           
3987 Nancy           Clyburn         Administration  Available           
3988 Jerry           Johnson         Sales           Available           
4067 Jack            Patel           Sales           Available           
4100 Jill            Brothers        IT              Available           
5678 Alice           Chang           Administration  Available           
5912 Alex            Smothers        IT              Available           
6754 Bob             Lucas           Sales           Available           
7145 Margaret        Sutton          Sales           Available           
8097 Lisa            Dominguez       Marketing       Available           
8564 Mark            Thompson        Marketing       Available           
9012 Mike            Nguyen          Marketing       Available           
9665 Jenny           Chavez          Administration  Available           
9873 Mary            Chavez          IT              Available           

cmd> list skill Sales     
list skill Sales
ID   First Name      Last Name       Skill           Assignment
2334 James           Bush            Sales           Available           
2870 Bonnie          Jones           Sales           Available           
3988 Jerry           Johnson         Sales           Available           
4067 Jack            Patel           Sales           Available           
6754 Bob             Lucas           Sales           Available           
7145 Margaret        Sutton          Sales           Available           

cmd> assign 3987 MaxRealty
assign 3987 MaxRealty

cmd> assign 4067 MaxRealty
assign 4067 MaxRealty

cmd> assign 2870 MaxRealty
assign 2870 MaxRealty

cmd> list assignment MaxRealty
list assignment MaxRealty
ID   First Name      Last Name       Skill           Assignment
3987 Nancy           Clyburn         Administration  MaxRealty           
2870 Bonnie          Jones           Sales           MaxRealty           
4067 Jack            Patel           Sales           MaxRealty           

cmd> unassign 2870
unassign 2870

cmd> list assignment MaxRealty
list assignment MaxRealty
ID   First Name      Last Name       Skill           Assignment
3987 Nancy           Clyburn         Administration  MaxRealty           
4067 Jack            Patel           Sales           MaxRealty           

cmd> quit
quit

As with recent projects, you'll be developing this one using git for revision control. You should be able to just unpack the starter into the p4 directory of your cloned repo to get started. See the Getting Started section for instructions.

This project supports a number of our course objectives. See the Learning Outcomes section for a list.

Rules for Project 4

You get to complete this project individually. If you're unsure what's permitted, you can have a look at the academic integrity guidelines in the course syllabus.

In the design section, you'll see some instructions for how your implementation is expected to work. Be sure you follow these rules. It's not enough to just turn in a working program; your program has to follow the design constraints we've asked you to follow. For this assignment, we're putting some constraints on the functions you'll need to define, the data structures you'll use and how you're going to organize your code into components. Still, you will have lots of opportunities to design parts of your solution and to create additional functions to simplify your implementation.

Requirements

This section says what your program is supposed to be able to do, and what it should do when something goes wrong.

Program Execution

The agency program expects one or more filenames on the command line. Each of these files should contain a list of employees that the program can read at startup. If the program is run with invalid command-line arguments (e.g., no filenames given on the command line), it should print the following usage message to standard error and exit with a status of 1.

usage: agency <employee-file>*

If the program can't open one of the given files for reading, it should print the following message to standard error and exit with a status of 1. Here, filename is the name of the file given on the command line. The program should report the first filename on the command line that it can't successfully open (i.e., if there are multiple filenames on the command line that can't be opened, it just needs to report this error for the first one that can't be opened).

Can't open file: filename

Employee List Format

At program start-up, the agency program reads in employees from one or more files and stores them in its database. On the command line, the program is given one or more filenames for files containing employee lists, stored in a particular format. Each line of an employee list file describes one employee. An employee description consists of four fields, with one or more spaces between the fields. The first field is a unique 4 digit employee ID number. Next there are three strings of up to 15 non-whitespace characters giving the employee's first name, last name, and skill, for example, Accounting, Administration, IT, Marketing, Sales, etc.

The program should process the employee list files in the same order they are given on the command line. Within each list, it should process employees in order from the first line to the last line of the file. An employee list file can contain any number of employee descriptions, one per line. If the format of the employee list is invalid (e.g., if a line is missing one of the expected fields, if an ID does not consist of exactly 4 digits, if a name/skill is too long), or if two or more employees have the same ID, then it should print the following message to standard error and exit with a status of 1. Here, filename is the name of the file containing the bad employee description.

Invalid employee file: filename

Employee List Output

A few user commands are available for listing employees. The output format for these lists is the same for all these commands. It gives one employee per line. Each employee is listed as an ID, followed by a first name, last name, and skill each left-aligned in a 15-charfacter field. These fields are followed by the name of the company to which they are currently assigned or the word "Available" if they are not assigned to a company. The company name will be left-aligned in a 20-character field. There will be one space separating each of these fields, so an entire output line will take 73 characters. At the top, the employee list should give a header like the one shown below. The header line won't need any space as the end, after the word "Assignment". Thethe other lines may have some spaces at the end, to fill in the 20-columns field width used for the last field.

ID   First Name      Last Name       Skill           Assignment
1245 Martha          Jones           Accounting      BusinessPros        
3897 Sanket          Patel           Marketing       Available           
7659 Jennifer        Chavez          IT              MaxRealty           

User Commands

After start-up, the agency program reads commands typed in by the user. Each command is given as single line of user input. For each command, the program will prompt the user with the following prompt. There's a space after the greater-than sign, but you probably can't see it in this web page.

cmd> 

After the user enters a command, the program will echo that command back to the user on the next output line (it will print a copy of the command the user just entered). This is mostly to help with debugging your programs. If we're capturing program output to a file, then things typed by the user don't go to the output file (user inputs show up on the terminal, but they're not part of the program's output). By echoing each command, our output files will include a copy of each command the user typed, making it easier to see what the program was asked to do. So, for example, if the user typed in a command like the following, the program would echo a copy of the command on the next line:

cmd> assign 3897 MaxReality
assign 3897 MaxReality

The user can type any of 4 available commands: list, assign, unassign, and quit. These commands are described below. Each valid command starts with one of the keywords listed above. For some commands, the keyword may be followed by one or more parameters. There may be one or more whitespace characters at the start of the command, between the keyword and the parameter(s) or at the end of the command.

If the user enters an invalid command, the program should print the following message to standard output (not standard error), ignore the command and prompt the user for another command.

Invalid command

Invalid commands would be those that start with something other than the 4 keywords listed above, or if the command's parameters weren't correct. In general, including a parameter when the command doesn't expect one, or extra text after the last parameter would also be considered invalid. The command descriptions below describe what each command expects as valid parameters.

After the first prompt, the program should print a blank line before prompting the user for another command. This is shown in the sample execution at the start of this project description. It's just to provide a little separation between the output for consecutive commands.

The program should terminate when it is given the quit command or when it reaches the end-of-file on standard input. In the case of the quit command, the program should echo the command back to the user (like all the other commands). In the case of end-of-file, there's no command to echo, so the program should just terminate without echoing anything.

List command

If the user enters the list command with no parameter after it, the program should print out all agency employees (all employees read at program start-up) in the format described in the "Employee List Output" section above. Employees should be sorted by their employee ID number.

cmd> list
list
ID   First Name      Last Name       Skill           Assignment
1245 Martha          Jones           Accounting      BusinessPros        
3897 Sanket          Patel           Marketing       Available           
7659 Jennifer        Chavez          IT              MaxRealty           

If the employee list is empty (if, say, the given employee list files were all empty) the program should just print out the header line for the employee list output.

List skill command

A second form of the list command takes an optional parameter, skill, followed by a sequence of non-whitespace characters to match in a skill field. For example, if the user enters the following command, then the program should just list the employees whose skill is Accounting. Employees should be sorted by their employee ID number.

cmd> list skill Accounting
list skill Accounting
ID   First Name      Last Name       Skill           Assignment
1245 Martha          Jones           Accounting      BusinessPros        
4578 James           Smith           Accounting      Available           

Matching for skill names is case sensitive, so, for example, listing the skill "Accounting" would give different results than listing the skill "ACCOUNTING".

If there are no employees in the list, or if none of their skills match the given string, then the program should just print out the header line shown in the "Employee List Output" section, with no employees listed under it.

The second parameter can be any sequence of up to 15 non-witespace characters. If the word is too long or if it contains spaces (i.e., if it's more than one word), then the command is invalid.

List assignment command

A third form of the list command takes an optional parameter, assignment, followed by a sequence of non-whitespace characters that matches an employee's assignment, which may be the name of a company or the word, Available, if they are not assigned to a company. For example, if the user enters the following command, then the program should just list the employees who are assigned to the given company. Employees should be sorted by their skill and then by their employee ID number.

cmd> list assignment MaxRealty
list assignment MaxRealty
ID   First Name      Last Name       Skill           Assignment
1234 Thomas          Baker           Administration  MaxRealty
4566 Jackie          Walters         Administration  MaxRealty
7659 Jennifer        Chavez          IT              MaxRealty

Matching an assignment name is case sensitive. For sorting the output, skills should be ordered lexicographically, in the same order given by strcmp. If there are no employees in the list, or if there are no employees whose assignment matches the second parameter, then the program should just print out the header line shown in the "Employee List Output" section, with no employees listed under it.

The second parameter can be any sequence of up to 20 non-whitespace characters. If the second parameter contains more than 20 characters, then the command is invalid.

Assign command

The assign command lets the user assign an employee to a given company. The assign command expects an ID as the first parameter and a company name as the second parameter. For example, the following command assigns the employee with ID 7659 to the company, MaxReality.

cmd> assign 7659 MaxRealty
assign 7659 MaxRealty

Other than echoing the user's command, the program doesn't need to print out any response to a valid assign command.

The assign command is invalid if its first parameter isn't an ID of an employee or if the employee is not Available (already assigned to a company, even if it's the given company). The second parameter can be any sequence of up to 20 non-whitespace characters. If the second parameter contains more than 20 characters, then the command is invalid.

Unassign command

The unassign command lets the user remove an employee from a company. As a parameter, it expects the ID of an employee that's currently assigned to a company. This command removes that employee from the company and sets the employee's assignment to Available.

cmd> unassign 7659
unassign 7659

Other than echoing the user's command, the program doesn't need to print out any response to a valid unassign command.

The unassign command must be given a parameter that matches the ID of an employee that's currently assigned to a company. Otherwise, it's considered an invalid command.

Quit command and termination

The quit command doesn't take any parameters. It should terminate the program. It's entered like the following:

cmd> quit

The program should also terminate successfully if it reaches the end-of-file on standard input while it's trying to read the next command.

Design

Program Organization

Your implementation will be organized into three components. The input component will help with reading input from the employee list files and it will be used to read commands from the user. The database component will contain code for reading and managing the list of employees read in at startup. The agency component is the top-level component, containing main() and code to parse user commands.

The following figure shows the organization of these components. The agency component will use code from the other two, and the database component will use input to help with reading lines from the employee list files.

Figure: components and dependency structure

Figure: components and dependency structure

Since the input and database components provide functions that are used elsewhere in the program, they will each have a header file that prototypes these functions. The database header will also define types (e.g., structs) used elsewhere in the program. The top-level component won't need a header.

Employee and Database Representation

This project is a good chance to get some experience using structs, dynamic memory allocation and resizable arrays. Each employee will be represented by a struct, Employee, with a field for its ID, first name, last name, skill, and assignment. All fields will be stored as strings.

Figure: Employee representation

Figure: Employee representation

The Database (the list of all employees) will be represented by its own struct, containing fields to store a resizable array of pointers to Employee instances. Each Employee will be stored in a block of dynamically allocated memory. Inside the Database struct, you will use a resizable array of pointers to Employees to keep up with all the employees read in at start-up. The count and capacity fields are for maintaining the resizable array, for keeping up with how many Employees are in the Database and for detecting when you run out of capacity and need to grow the array. Your resizable array should start with an initial capacity of 5, and it should double in capacity whenever the array needs to be enlarged.

Figure: Database representation

Figure: Database representation

Expected Functions

As part of your implementation, you will define and use the following functions. You can define more if you want to. Just try to put them in a component that's suitable for whatever they do and remember to mark them as static where you can (i.e., if they're not used outside the component where they're defined).

Your input component only needs to have one function. It's kind of like the function we used in project 3, but this one is better. It can handle input lines of arbitrary length.

Your database component should have the following five functions.

Your agency component will contain main() and any other functions you need to parse command-line arguments and user commands.

Parsing User Commands

With help from your input component, it will be easy to read user input one line at a time. After you get a line of input from the user, you will need to look inside this string to figure out what command the user typed. The sscanf() function will make it easy to do this. It works much like scanf() or fscanf(), but it parses input from a string instead of from a file. We'll cover this function in lecture 19, but you may want to look at the material for lecture 19 early (it should already be posted) so you can get started on the project earlier.

Remember, unlike reading from a file, sscanf() doesn't automatically resume parsing from where it left off on the last call. For example, you couldn't call something like sscanf( str, "%d", &x ); to read an int then call sscanf( str, "%d", &y ); again to read the next int. If you gave sscanf() the same string in two successive calls, it would just start parsing at the start of the string each time. If you want to parse multiple values out of the same string, you can do it all at once, like sscanf( str, "%d%d", &x, &y );, or you could advance the pointer on successive calls to sscanf(), as in sscanf( str + offset, "%d", &y );. If you need to do this, the %n conversion specifier we covered in lecture 10 can be helpful.

Function Visibility

Any functions that are needed by a different component should be prototyped (and commented) in the header. Functions that don't need to be used by a different component should not be prototyped in the header, and should be marked static (given internal linkage), so they won't be visible to any other part of the program. This is like making the function an implementation detail of its component, something we could change if we wanted without affecting other parts of the program.

Sorting Employees

You'll use the standard library qsort() function to sort the array of employee pointers inside the database struct, ordering them either by id (for the list and list skill commands) or by skill followed by id (for the list assignment command). Using qsort() will make the sorting easier (and probably more efficient), but you have to help out qsort() by providing a pointer to a comparison function. We have some examples of this in the material from lecture 13, in the slides and in the sort.c example program.

To use qsort() you'll need to think about a few things. As usual, you'll need to write your own comparison function(s). These functions will have to be declared to take two (const) void pointers as parameters. However, the functions will know the specific type of data that these pointers really point to. You'll use qsort() to sort your array of pointers to Employees, so the void pointers qsort() passes to your compare function will really be pointers to two of these array elements. The array elements are all pointers to Employees, so the parameters to a comparison function will really be pointers to pointers to Employees. Inside the function, you can convert the two void pointers to the right type for what they're really pointing to. Then, you can use them to access fields in Employee structs and decide how they compare. As we discussed in class, your comparison function should return -1 if the employee given by the fist parameter should be ordered before the second one, it should return 1 if the employee given by the first parameter should come after the second one, and it should return 0 if they're considered equal.

The listEmployees() function expects a compare function pointer as a parameter. It will pass in this function as the last parameter to qsort(). This way, code elsewhere in the program can use listEmployees() function to print out employees in any order it wants, by providing listEmployees() with a pointer to an appropriate comparison function. You can define the comparison functions you need in your top-level agency component. You can make them static. Other components won't be able to call these functions by name, but listEmployees() will still be able to call a static comparison function if you pass in a pointer to it.

Selecting Employees to Report

The listEmployees() function can be used to print all the employees in the database, or just selected ones. This will let us use it to implement the list command, with or without a string to match.

How does listEmployees() know which employees to report? Internally, it will call the provided test function for each Employee in the database with the provided string. If the test function returns true, listEmployees()should print that Employee; otherwise, it shouldn't. This lets client code use a single interface to print any subset of the Employees. The client code just needs to provide a pointer to a function listEmployees() can use to decide what to print and what not to print.

To print all Employees, you can pass in a pointer to a test function that always returns true and simply provide NULL as the string parameter. To print Employees that match a particular skill, you can pass in a pointer to a function that checks if the provided string matches the Employee's skill. Likewise, to print Employees that match a particular assignment, you can pass in a pointer to a function that checks if the provided string matches the Employee's assignment.

Output Alignment

Normally, printf will right-align output values in a given field width. You can use a negative value for the field width to get it to left align instead.

Build Automation

You get to implement your own Makefile for this project (called Makefile with a capital 'M', no filename extension). Its default target should build your program, compiling each source file to an object file and then linking the objects together into an executable.

As usual, your Makefile should correctly describe the project's dependencies, so targets can be rebuilt selectively, based on what source files have changed. It should also have a clean rule, to let the user easily delete any temporary files or target files that can be rebuilt the next time make is run (e.g., the object files, the executable and any temporary output files).

In addition to the "-Wall" and "-std=c99" options that we normally include when we're compiling, be sure to include the "-g" flag. This will be useful when you try to use gdb or valgrind to help debug the program.

Testing

The starter includes a test script, along with test input files and expected outputs. When we grade your program, we'll test it with this script, along with a few other test inputs we're not giving you. To run the automated test script, you should be able to enter the following:

$ chmod +x test.sh # probably just need to do this once
$ ./test.sh

This will automatically build your program (using your Makefile) and see how it does against all the tests.

As you develop your program, you'll want to try it out with user input and see what it's doing on individual test cases. Until your Makefile is working, you should be able to execute the compiler directly with the following command (although this isn't as efficient as how your Makefile builds).

$ gcc -g -Wall -std=c99 agency.c database.c input.c -o agency

To try out your program on one of the provided test cases, you can run it as follows. Here, we're saving the program's standard output and standard error to two different files. All of the tests have an expected output file for stdout, and some of them (the ones that exit unsuccessfully) also have an expected error output to stderr.

Here, we're running test 06 by hand. After running the program, we check its exit status to make sure it ran successfully (it should for this test). Then we check the output file to make sure we got what was expected. This particular test shouldn't print any output to the standard error stream, but error tests will.

./agency list-b.txt list-c.txt < input-06.txt > output.txt 2> stderr.txt
$ echo $?
0
$ diff output.txt expected-06.txt
$ cat stderr.txt

Memory Error and Leaks

On any test that runs successfully, your program is expected to free all of the dynamically allocated memory it allocates and close any files it opens. Although it's not part of an automated test, we encourage you to try out your executable with valgrind. We certainly will when we're grading your work. Any leaked memory, use of uninitialized memory, access to memory outside the range of an allocated block or leaked files will cost you some points. Valgrind can help you find these errors before we do.

Your program is not expected to free memory or close all its files on error tests, those tests where your program is supposed to exit unsuccessfully. On these test cases, your program may need to exit from inside some function, where you don't have access to all the memory you've allocated or all the files you've opened. In these cases, your program can just exit right away, without having to free all resources.

The compile instructions above include the -g flag when building your program. This will help valgrind give more useful reports of where it sees errors. To get valgrind to check for memory errors, including memory leaks, you can run your program like the following. This example runs the program on input 09, a test input that uses several of the supported commands. You can use similar commands to try your program on any input using valgrind.

$ valgrind --leak-check=full --show-leak-kinds=all --track-fds=yes ./agency list-b.txt list-c.txt < input-09.txt
... lots of output ...

If you want to run valgrind, you have to run it on each test case individually. You can't run the test script inside valgrind (well, you can, but then you will be getting valgrind output for the shell, rather than for the program you wrote). I've seen students try to run something like "valgrind test.sh". Just be aware that this won't work. You have to run valgrind like the example above.

Test Cases

The tests for your program use the following employee list files. These contain various-sized lists of employees, along with some invalid lists for testing error handling.

We've prepared 21 tests for your program. Using the employee lists above, they exercise the various commands your program is supposed to support, working from the easier ones to the more difficult tests and error cases. In developing your program, you may want to follow the order of these tests, adding the code to support the first test, then working toward the later (more complex) tests as you get the earlier ones working.

  1. This test reads the list-a.txt file, but it runs the quit command immediately.

  2. This test reads the list-b.txt file. It runs the list command then input ends at the end-of-file on the input, rather than a quit command (which should be OK).

  3. This test reads the list-b.txt and list-a.txt files. This requires the program to read two employee files and it should require growing of the resizable array in Database. It uses the list and quit commands.

  4. This test reads the list-b.txt and list-c.txt files and uses the list command. This requires growing the resizable array twice and it requires being able to sort employees by id.

  5. This test reads the list-b.txt list-c.txt files. It then lists employees with a given skill.

  6. This test reads the list-b.txt list-c.txt files. It then lists employees with the assignment, Available, which requires sorting the employees by skill and then by id before listing them.

  7. This test reads the list-b.txt and list-c.txt files. It then assigns several employees to companies and then lists all employees.

  8. This test reads the list-b.txt list-c.txt files. It then assigns several employees to companies, unassigns some of them and then lists all employees.

  9. This is the example shown at the start of the assignment. It uses several different commands.

  10. This test reads the list-d.txt file and then lists all employees with a given skill.

  11. This test reads the list-d.txt file and assigns some employees to various companies, then lists the employees assigned to each of the companies.

  12. This test reads the list-d.txt file and assigns some employees to various companies, unassigns some of them, then lists the employees assigned to each of the companies.

  13. This test reads the list-a, list-b.txt, and list-c.txt files and performs a variety of commands.

  14. This test contains a few bad commands. They shouldn't terminate the program, but they should get an Invalid Command message.

  15. This test includes some list commands that don't match anything and list commands with bad parameters.

  16. This is an error test, with no files given on the command line.

  17. This is an error test. A filename given on the command line doesn't exist.

  18. This is an error test. The employee list contains a last name that's too long.

  19. This is an error test. One line of the employee list is missing a field.

  20. This is an error test. One line of the employee list has an id that does not contain exactly 4 digits.

  21. This is an error test. Two employees have the same id number.

Grading

The grade for your program will depend mostly on how well it functions. You also get to provide your own Makefile, and we'll expect your program to compile cleanly, to follow the style guide and to adhere to the expected design.

Getting Started

To get started on this project, you'll need to clone your NCSU github repo and unpack the given starter into the p4 directory of your repo. You'll submit by committing files to your repo and pushing the changes back up to the NCSU github.

Clone your Repository

You should have already cloned your assigned NCSU github repo when you were working on project 2. If you haven't already done this, go back to the assignment for project 2 and follow the instructions for for cloning your repo.

Unpack the starter into your cloned repo

You will need to copy and unpack the project 4 starter. We're providing this file as a compressed tar archive, starter4.tgz. You can get a copy of the starter by using the link in this document, or you can use the following curl command to download it from your shell prompt.

$ curl -O https://www.csc2.ncsu.edu/courses/csc230/proj/p4/starter4.tgz

Temporarily, put your copy of the starter in the p4 directory of your cloned repo. Then, you should be able to unpack it with the following command:

$ tar xzvpf starter4.tgz

Once you start working on the project, be sure you don't accidentally commit the starter to your repo. After you've successfully unpacked it, you may want to delete the starter from your p4 directory, or move it out of your repo.

$ rm starter4.tgz

Instructions for Submission

If you've set up your repository properly, pushing your changes to your assigned CSC230 repository should be all that's required for submission. When you're done, we're expecting your repo to contain the following files. You can use the web interface on github.ncsu.edu to confirm that the right versions of all your files made it.

Pushing your Changes

To submit your project, you'll need to commit your changes to your cloned repo, then push them to the NCSU github. Project 2 has more detailed instructions for doing this, but I've also summarized them here.

Whenever you create a new file that needs to go into your repo, you need to stage it for the next commit using the add command:

$ git add some-new-file

Then, before you commit, it's a good idea to check to make sure your index has the right files staged:

$ git status

Once you've added any new files, you can use a command like the following to commit them, along with any changes to files that were already being tracked:

$ git commit -am "<meaningful message for future self>"

Of course, you haven't really submitted anything until you push your changes up to the NCSU github:

$ git push

Checking Jenkins Feedback

Checking Jenkins feedback is similar to previous projects. Visit our Jenkins system at http://go.ncsu.edu/jenkins-csc230 and you'll see a new build job for project 4. This job polls your repo periodically for changes and rebuilds and tests your project automatically whenever it sees a change.

Learning Outcomes

The syllabus lists a number of learning outcomes for this course. This assignment is intended to support several of theses: