Removed submodules
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
# Ignore all compiled files regardless of location
|
||||||
|
*.o
|
||||||
|
tests/public.o
|
||||||
|
tests/testsuite.o
|
||||||
|
|
||||||
|
# But not the private.o
|
||||||
|
!tests/private.o
|
||||||
|
|
||||||
|
# Ignore executables for this project
|
||||||
|
ptrs
|
||||||
|
testsuite
|
||||||
|
|
||||||
|
# Ignore test outputs
|
||||||
|
tests/ckstyle
|
||||||
|
tests/itests.txt
|
||||||
|
tests/outputs
|
||||||
|
tests/style.txt
|
||||||
|
tests/valgrind
|
||||||
|
tests/utests.txt
|
||||||
|
|
||||||
|
**/.nfs*
|
||||||
|
**/.vscode
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# Simple Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This makefile builds a simple application that contains a main module
|
||||||
|
# (specified by the EXE variable) and a predefined list of additional modules
|
||||||
|
# (specified by the MODS variable). If there are any external library
|
||||||
|
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
|
||||||
|
# If there are any precompiled object files, list them in the OBJS variable.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=ptrs
|
||||||
|
MODS=movies.o
|
||||||
|
OBJS=
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
default: $(EXE)
|
||||||
|
|
||||||
|
test: $(EXE)
|
||||||
|
make -C tests test
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall -Werror --std=c99
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(EXE): main.o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXE) main.o $(MODS)
|
||||||
|
make -C tests clean
|
||||||
|
|
||||||
|
.PHONY: default clean
|
||||||
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "movies.h"
|
||||||
|
|
||||||
|
int cmdline (int, char **, bool *);
|
||||||
|
|
||||||
|
void
|
||||||
|
usage (void)
|
||||||
|
{
|
||||||
|
printf ("Usage: ptrs [-hm]\n");
|
||||||
|
printf (" Options are:\n");
|
||||||
|
printf (" -m Parse the CSV data as a movie\n");
|
||||||
|
printf (" -h Print this help message\n");
|
||||||
|
printf ("Only one option is allowable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool run_movie = false;
|
||||||
|
|
||||||
|
if (cmdline (argc, argv, &run_movie) != 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (!run_movie)
|
||||||
|
{
|
||||||
|
usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the following comma-separated value (CSV) data into fields
|
||||||
|
char *data = "The Shawshank Redemption,1994,Drama";
|
||||||
|
movie_t movie = split_data (data);
|
||||||
|
printf ("Title: %s\n", movie.title);
|
||||||
|
printf ("Year: %d\n", movie.year);
|
||||||
|
printf ("Genre: %s\n", movie.genre);
|
||||||
|
|
||||||
|
char *rebuilt = merge_data (movie);
|
||||||
|
printf ("%s\n", rebuilt);
|
||||||
|
|
||||||
|
free (rebuilt);
|
||||||
|
free (movie.title);
|
||||||
|
free (movie.genre);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get
|
||||||
|
the signal numbers to override, the number of overridden signals, and
|
||||||
|
the order of signals to raise in the full implementation. */
|
||||||
|
int
|
||||||
|
cmdline (int argc, char **argv, bool *movies)
|
||||||
|
{
|
||||||
|
int option;
|
||||||
|
|
||||||
|
if ((option = getopt (argc, argv, "mh")) == -1)
|
||||||
|
{
|
||||||
|
usage ();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
*movies = false;
|
||||||
|
return -1;
|
||||||
|
case 'm':
|
||||||
|
*movies = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf ("Invalid command-line option: %c\n", optopt);
|
||||||
|
usage ();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L // needed for strdup extension
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "movies.h"
|
||||||
|
|
||||||
|
/* Given a string (possibly read-only) of IMDB movie data
|
||||||
|
(formatted as "Title,Year,Genre"), create a movie_t object
|
||||||
|
based on the appropriate fields. */
|
||||||
|
movie_t
|
||||||
|
split_data (char *csv)
|
||||||
|
{
|
||||||
|
movie_t movie;
|
||||||
|
char *temp = strdup (csv);
|
||||||
|
char *token = strtok (temp, ",");
|
||||||
|
movie.title = strdup (token); // make a copy the first token as the title
|
||||||
|
|
||||||
|
char *year_str = strtok (NULL, ",");
|
||||||
|
char *endptr;
|
||||||
|
movie.year = strtol (year_str, &endptr, 10);
|
||||||
|
|
||||||
|
char *genre = strtok (NULL, ",");
|
||||||
|
movie.genre = strdup (genre);
|
||||||
|
|
||||||
|
free (temp);
|
||||||
|
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build a dynamically allocated string from a movie_t object as follows:
|
||||||
|
"Title [Year] - Genre"
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
merge_data (movie_t movie)
|
||||||
|
{
|
||||||
|
// There are a few ways to merge strings in C. One approach is to start with
|
||||||
|
// a base string on the heap and repeatedly grow and append the string. In
|
||||||
|
// pseudocode, it might look like this:
|
||||||
|
//
|
||||||
|
// str = strdup ("Hello");
|
||||||
|
// str = realloc (str, ...length...); // grow
|
||||||
|
// strncat (str, " World", ...length...); // concatenate
|
||||||
|
// str = realloc (str, ...length...); // grow
|
||||||
|
// strncat (str, " Again", ...length...); // concatenate
|
||||||
|
//
|
||||||
|
// This is the C equivalent of something like this in Java:
|
||||||
|
//
|
||||||
|
// str = "Hello";
|
||||||
|
// str += " World";
|
||||||
|
// str += " Again";
|
||||||
|
//
|
||||||
|
// Another (generally more efficient) way to do this is to use snprintf(),
|
||||||
|
// for formatting a string. If you are not familiar with snprintf(), you
|
||||||
|
// should be familiar with its cousin, printf(). Consider the following
|
||||||
|
// line of code, which prints three string variables to the screen:
|
||||||
|
//
|
||||||
|
// printf ("%s %s %s\n", str1, str2, str3);
|
||||||
|
//
|
||||||
|
// You do the exact same thing with snprintf(), but add two variables at
|
||||||
|
// the beginning: the destination buffer (i.e., where do you want the
|
||||||
|
// final string in memory) and the length:
|
||||||
|
//
|
||||||
|
// snprintf (buffer, length, "%s %s %s\n", str1, str2, str3);
|
||||||
|
//
|
||||||
|
// The original strings str1, str2, and str3 are unmodified, because their
|
||||||
|
// contents are copied into the buffer. snprintf() also has a great built-in
|
||||||
|
// feature that strncat() doesn't: it works with more than just strings!
|
||||||
|
//
|
||||||
|
// int num = 42;
|
||||||
|
// char *str = "My favorite number is ";
|
||||||
|
// snprintf (message, length, "%s%d\n", str, num);
|
||||||
|
//
|
||||||
|
// Just remember that the message/buffer variable must be a pointer to a
|
||||||
|
// writable portion of memory where you have allocated enough space to store
|
||||||
|
// the combined string.
|
||||||
|
|
||||||
|
// HINT: When doing string manipulation, you should use a variable to keep
|
||||||
|
// explicit track of the string length. C string lengths are not inherently
|
||||||
|
// stored anywhere.
|
||||||
|
size_t len = 1; // always keep track of the null byte
|
||||||
|
|
||||||
|
// +10 byte space for yeaar, spaces, brackets, and dash
|
||||||
|
len += strlen (movie.title) + 10 + strlen (movie.genre);
|
||||||
|
|
||||||
|
// Use one of the two techniques above to allocated enough space for the
|
||||||
|
// merged string. You should NOT rely on any length unless its exact value
|
||||||
|
// is known. For instance, you can assume that " [Year] - " adds 10 bytes,
|
||||||
|
// but you cannot assume anything about the length of the genre or title.
|
||||||
|
// As such, you need to compute those lengths using strlen(). Do not forget
|
||||||
|
// to account for the null byte.
|
||||||
|
char *result = calloc (len, sizeof (char));
|
||||||
|
snprintf (result, len, "%s [%d] - %s", movie.title, movie.year, movie.genre);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef __CS361_MOVIES_H__
|
||||||
|
#define __CS361_MOVIES_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct movie
|
||||||
|
{
|
||||||
|
char *title;
|
||||||
|
int year;
|
||||||
|
char *genre;
|
||||||
|
} movie_t;
|
||||||
|
|
||||||
|
movie_t split_data (char *);
|
||||||
|
char *merge_data (movie_t);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#
|
||||||
|
# Simple Test Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This version of the Makefile includes support for building a test suite. The
|
||||||
|
# recommended framework is Check (http://check.sourceforge.net/). To build and
|
||||||
|
# run the test suite, execute the "test" target. The test suite must be located
|
||||||
|
# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as
|
||||||
|
# they do in the main Makefile.
|
||||||
|
#
|
||||||
|
# To change the default build target (which executes when you just type
|
||||||
|
# "make"), change the right-hand side of the definition of the "default"
|
||||||
|
# target.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=../ptrs
|
||||||
|
TEST=testsuite
|
||||||
|
MODS=public.o
|
||||||
|
OBJS=../movies.o
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
UTESTOUT=utests.txt
|
||||||
|
ITESTOUT=itests.txt
|
||||||
|
SCHECKOUT=style.txt
|
||||||
|
|
||||||
|
default: $(TEST)
|
||||||
|
|
||||||
|
$(EXE):
|
||||||
|
make -C ../
|
||||||
|
|
||||||
|
test: utest itest style
|
||||||
|
@echo "========================================"
|
||||||
|
|
||||||
|
utest: $(EXE) $(TEST)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " UNIT TESTS"
|
||||||
|
@./$(TEST) 2>/dev/null >$(UTESTOUT)
|
||||||
|
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
|
||||||
|
|
||||||
|
itest: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " INTEGRATION TESTS"
|
||||||
|
@./integration.sh | tee $(ITESTOUT)
|
||||||
|
|
||||||
|
style: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " CODING STYLE CHECK"
|
||||||
|
@./style.sh 2>/dev/null >$(SCHECKOUT)
|
||||||
|
@cat $(SCHECKOUT)
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall --std=c99 -pedantic
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
#LDFLAGS+=-L/opt/local/lib
|
||||||
|
LDFLAGS=
|
||||||
|
LIBS+=-lcheck -lm -lpthread
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Linux)
|
||||||
|
LIBS+=-lrt -lsubunit
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(TEST): $(TEST).o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle
|
||||||
|
|
||||||
|
.PHONY: default clean test unittest inttest
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
514af63cc957d92dab366709ad95b76ad030e9d9
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
514af63cc957d92dab366709ad95b76ad030e9d9
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Title: The Shawshank Redemption
|
||||||
|
Year: 1994
|
||||||
|
Genre: Drama
|
||||||
|
The Shawshank Redemption [1994] - Drama
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Title: The Shawshank Redemption
|
||||||
|
Year: 1994
|
||||||
|
Genre: Drama
|
||||||
|
The Shawshank Redemption [1994] - Drama
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
EXE="../ptrs"
|
||||||
|
|
||||||
|
function run_test {
|
||||||
|
|
||||||
|
# parameters
|
||||||
|
TAG=$1
|
||||||
|
ARGS=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
OUTPUT=outputs/$TAG.txt
|
||||||
|
DIFF=outputs/$TAG.diff
|
||||||
|
EXPECT=expected/$TAG.txt
|
||||||
|
VALGRND=valgrind/$TAG.txt
|
||||||
|
|
||||||
|
# print tag format
|
||||||
|
PTAG=$(printf '%-30s' "$TAG")
|
||||||
|
|
||||||
|
# check for expected text that needs to be fixed
|
||||||
|
if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# compare the file
|
||||||
|
SHAOUT=$(shasum "$EXPECT" | awk '{print $1}')
|
||||||
|
SHAEXP=$(cat "expected/.$TAG.sha")
|
||||||
|
if [ "$SHAOUT" != "$SHAEXP" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run test and compare output to the expected version
|
||||||
|
$EXE "$ARGS" 2>/dev/null >"$OUTPUT"
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
|
||||||
|
# try alternative solution (if it exists)
|
||||||
|
EXPECT=expected/$TAG-2.txt
|
||||||
|
if [ -e "$EXPECT" ]; then
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run valgrind
|
||||||
|
valgrind $EXE $ARGS &>$VALGRND
|
||||||
|
}
|
||||||
|
|
||||||
|
# initialize output folders
|
||||||
|
mkdir -p outputs
|
||||||
|
mkdir -p valgrind
|
||||||
|
rm -f outputs/* valgrind/*
|
||||||
|
|
||||||
|
# run individual tests
|
||||||
|
source itests.include
|
||||||
|
|
||||||
|
# check for memory leaks
|
||||||
|
LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'`
|
||||||
|
if [ -z "$LEAK" ]; then
|
||||||
|
echo "No memory leak found."
|
||||||
|
else
|
||||||
|
echo "Memory leak(s) found. See files listed below for details."
|
||||||
|
grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g'
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# list of integration tests
|
||||||
|
# format: run_test <TAG> <ARGS>
|
||||||
|
# <TAG> used as the root for all filenames (i.e., "expected/$TAG.txt")
|
||||||
|
# <ARGS> command-line arguments to test
|
||||||
|
|
||||||
|
run_test INTEG_movies "-m"
|
||||||
|
run_test INTEG_movies_inc "-m"
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
#include "../movies.h"
|
||||||
|
|
||||||
|
// Test that split_data gets the title copy
|
||||||
|
START_TEST (UNIT_split_title)
|
||||||
|
{
|
||||||
|
char *csv = "Casablanca,1942,Romance";
|
||||||
|
movie_t movie = split_data (csv);
|
||||||
|
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
|
||||||
|
ck_assert_str_eq (movie.title, "Casablanca");
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
// Test that split_data parses numeric values
|
||||||
|
START_TEST (UNIT_split_nums)
|
||||||
|
{
|
||||||
|
char *csv = "Casablanca,1942,Romance";
|
||||||
|
movie_t movie = split_data (csv);
|
||||||
|
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
|
||||||
|
ck_assert_int_eq (movie.year, 1942);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
// Test that split_data gets the genre copy
|
||||||
|
START_TEST (UNIT_split_genre)
|
||||||
|
{
|
||||||
|
char *csv = "Casablanca,1942,Romance";
|
||||||
|
movie_t movie = split_data (csv);
|
||||||
|
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
|
||||||
|
ck_assert_str_eq (movie.genre, "Romance");
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
// Test that split_data operates correctly on stack-based strings
|
||||||
|
START_TEST (UNIT_split_stack)
|
||||||
|
{
|
||||||
|
|
||||||
|
char csv[] = "Casablanca,1942,Romance";
|
||||||
|
movie_t movie = split_data (csv);
|
||||||
|
ck_assert_str_eq (movie.title, "Casablanca");
|
||||||
|
ck_assert_int_eq (movie.year, 1942);
|
||||||
|
ck_assert_str_eq (movie.genre, "Romance");
|
||||||
|
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
// Test that merge_data combines the fields into a string
|
||||||
|
START_TEST (UNIT_merge_movie)
|
||||||
|
{
|
||||||
|
movie_t movie;
|
||||||
|
movie.title = "The Lord of the Rings: The Fellowship of the Ring";
|
||||||
|
movie.year = 2001;
|
||||||
|
movie.genre = "Fantasy";
|
||||||
|
char *result = merge_data (movie);
|
||||||
|
ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy");
|
||||||
|
free (result);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
// Test that merge_data combines the fields from the heap into a string
|
||||||
|
START_TEST (UNIT_merge_heap)
|
||||||
|
{
|
||||||
|
movie_t *movie = calloc (1, sizeof (movie_t));
|
||||||
|
movie->title = "The Lord of the Rings: The Fellowship of the Ring";
|
||||||
|
movie->year = 2001;
|
||||||
|
movie->genre = "Fantasy";
|
||||||
|
char *result = merge_data (*movie);
|
||||||
|
ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy");
|
||||||
|
free (result);
|
||||||
|
result = NULL;
|
||||||
|
ck_assert_str_eq (movie->title, "The Lord of the Rings: The Fellowship of the Ring");
|
||||||
|
ck_assert_int_eq (movie->year, 2001);
|
||||||
|
ck_assert_str_eq (movie->genre, "Fantasy");
|
||||||
|
free (movie);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
void public_tests (Suite *s)
|
||||||
|
{
|
||||||
|
TCase *tc_public = tcase_create ("Public");
|
||||||
|
tcase_add_test (tc_public, UNIT_split_title);
|
||||||
|
tcase_add_test (tc_public, UNIT_split_nums);
|
||||||
|
tcase_add_test (tc_public, UNIT_split_genre);
|
||||||
|
tcase_add_test (tc_public, UNIT_split_stack);
|
||||||
|
tcase_add_test (tc_public, UNIT_merge_movie);
|
||||||
|
tcase_add_test (tc_public, UNIT_merge_heap);
|
||||||
|
suite_add_tcase (s, tc_public);
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STYLE="gnu"
|
||||||
|
|
||||||
|
IGNORE=()
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
function comp_file {
|
||||||
|
|
||||||
|
SRC=$1
|
||||||
|
SRC_NAME=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
FORMAT=ckstyle/${SRC_NAME}.$STYLE
|
||||||
|
DIFF=ckstyle/${SRC_NAME}.diff
|
||||||
|
|
||||||
|
# run clang-format and compare results
|
||||||
|
clang-format --style=$STYLE $source > $FORMAT
|
||||||
|
diff -u $SRC $FORMAT >$DIFF
|
||||||
|
|
||||||
|
PTAG=$(printf '%-30s' "$SRC_NAME")
|
||||||
|
if [ -s $DIFF ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
FAIL=1
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
rm $DIFF
|
||||||
|
fi
|
||||||
|
rm $FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p ckstyle
|
||||||
|
rm -f ckstyle/*
|
||||||
|
|
||||||
|
for source in $(ls ../*.c ../*.h) ; do
|
||||||
|
SKIP=0
|
||||||
|
src=$(basename $source)
|
||||||
|
for ignore in ${IGNORE[*]} ; do
|
||||||
|
if [ "$src" = "$ignore" ] ; then
|
||||||
|
SKIP=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $SKIP = 0 ] ; then
|
||||||
|
comp_file $source $src
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAIL != 0 ] ; then
|
||||||
|
echo "Code that does not adhere to GNU standards will not be accepted."
|
||||||
|
echo "You must fix these files before submission."
|
||||||
|
else
|
||||||
|
echo "Code correctly adheres to required style."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
extern void public_tests (Suite *s);
|
||||||
|
void run_testsuite (void);
|
||||||
|
|
||||||
|
Suite *
|
||||||
|
test_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("Default");
|
||||||
|
public_tests (s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_testsuite (void)
|
||||||
|
{
|
||||||
|
Suite *s = test_suite ();
|
||||||
|
SRunner *sr = srunner_create (s);
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
srunner_free (sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
run_testsuite ();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Ignore all compiled files regardless of location
|
||||||
|
**/*.o
|
||||||
|
|
||||||
|
# But not the private.o
|
||||||
|
!tests/private.o
|
||||||
|
|
||||||
|
# Ignore executables for this project
|
||||||
|
shell
|
||||||
|
testsuite
|
||||||
|
|
||||||
|
# Ignore test outputs
|
||||||
|
tests/ckstyle
|
||||||
|
tests/itests.txt
|
||||||
|
tests/outputs
|
||||||
|
tests/style.txt
|
||||||
|
tests/valgrind
|
||||||
|
tests/utests.txt
|
||||||
|
|
||||||
|
**/.nfs*
|
||||||
|
**/.vscode
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# Simple Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This makefile builds a simple application that contains a main module
|
||||||
|
# (specified by the EXE variable) and a predefined list of additional modules
|
||||||
|
# (specified by the MODS variable). If there are any external library
|
||||||
|
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
|
||||||
|
# If there are any precompiled object files, list them in the OBJS variable.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=shell
|
||||||
|
MODS=model.o effects.o
|
||||||
|
OBJS=
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
default: $(EXE)
|
||||||
|
|
||||||
|
test: $(EXE)
|
||||||
|
make -C tests test
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(EXE): main.o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXE) main.o $(MODS)
|
||||||
|
make -C tests clean
|
||||||
|
|
||||||
|
.PHONY: default clean
|
||||||
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L // needed for strdup extension
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "effects.h"
|
||||||
|
#include "model.h"
|
||||||
|
|
||||||
|
/* Executed when starting to process a new command line. The fsm_t
|
||||||
|
should have been updated to include a pointer to the current token.
|
||||||
|
For instance, if the command line was "ls -l data NL", the fsm_t
|
||||||
|
has a field that points to "ls". */
|
||||||
|
void
|
||||||
|
start_command (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
printf ("Starting new command: %s\n", cmdmodel->current_token);
|
||||||
|
// TODO: Copy the current token to store it in the FSM's command
|
||||||
|
// field. Next, create the FSM's args array (length MAX_ARGUMENTS)
|
||||||
|
// set the current token as args[0], and initialize nargs to be
|
||||||
|
// the number of arguments (1 at this point).
|
||||||
|
// Allocate args array (NULL-initialized) if not already
|
||||||
|
cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *));
|
||||||
|
cmdmodel->command = cmdmodel->current_token;
|
||||||
|
cmdmodel->args[0] = cmdmodel->current_token;
|
||||||
|
cmdmodel->nargs = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Executed when processing a token after the command name. For instance,
|
||||||
|
if the command line was "ls -l data NL", this function will be called
|
||||||
|
when the current token is "-l" and again when it is "data". */
|
||||||
|
void
|
||||||
|
append (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
if (cmdmodel->nargs >= MAX_ARGUMENTS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf ("Appending %s to the argument list\n", cmdmodel->current_token);
|
||||||
|
assert (cmdmodel->args != NULL);
|
||||||
|
|
||||||
|
// TODO: Store the current token into the args array and increment nargs
|
||||||
|
cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Executed when either a NL or | (pipe) is encountered. For instance, if
|
||||||
|
the command line is "ls -l data NL", the current token will be "NL"; also,
|
||||||
|
the FSM's args array should be complete, containing "ls", "-l", and "data",
|
||||||
|
followed by several NULL pointers. */
|
||||||
|
void
|
||||||
|
execute (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
assert (cmdmodel->args != NULL);
|
||||||
|
|
||||||
|
// TODO: Print out the argument list similar to the format shown and free
|
||||||
|
// the args array.
|
||||||
|
printf ("Execute %s with arguments { ", cmdmodel->args[0]);
|
||||||
|
for (uint8_t i = 0; i < cmdmodel->nargs; i++)
|
||||||
|
{
|
||||||
|
printf ("%s, ", cmdmodel->args[i]);
|
||||||
|
}
|
||||||
|
printf ("(null) }\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// No changes are needed to the effects below
|
||||||
|
|
||||||
|
void
|
||||||
|
link_commands (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
printf ("Set up pipe\n");
|
||||||
|
execute (cmdmodel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error_pipe (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
printf ("ERROR: Received token %s while in state %s\n",
|
||||||
|
cmdmodel->current_token, state_name (cmdmodel->state));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error_newline (fsm_t *cmdmodel)
|
||||||
|
{
|
||||||
|
printf ("ERROR: Received token %s while in state %s\n",
|
||||||
|
cmdmodel->current_token, state_name (cmdmodel->state));
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef __effects_h__
|
||||||
|
#define __effects_h__
|
||||||
|
|
||||||
|
#include "model.h"
|
||||||
|
|
||||||
|
void start_command (fsm_t *);
|
||||||
|
void append (fsm_t *);
|
||||||
|
void execute (fsm_t *);
|
||||||
|
void link_commands (fsm_t *);
|
||||||
|
void error_pipe (fsm_t *);
|
||||||
|
void error_newline (fsm_t *);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* CS 361: Template lab driver
|
||||||
|
*
|
||||||
|
* Name:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "model.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
usage (void)
|
||||||
|
{
|
||||||
|
printf ("shell, a command line state model simulator\n\n");
|
||||||
|
printf ("usage: shell cmd [arg ...] [ | cmd [arg ...] ... ]\n");
|
||||||
|
printf ("Each cmd can contain an argument list of variable length\n");
|
||||||
|
printf ("Each cmd after the first must be preceded by a pipe | character\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
// With no arguments, show usage and exit
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare and initialize a FSM
|
||||||
|
fsm_t *cmdmodel = cmdline_init ();
|
||||||
|
if (cmdmodel == NULL)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// TODO: Change this to split the string into tokens, where
|
||||||
|
// each token is an event that needs to be handled. After
|
||||||
|
// looking up the event number, store the token in the FSM
|
||||||
|
// and call handle_event().
|
||||||
|
char *token = strtok (argv[1], " ");
|
||||||
|
while (token != NULL)
|
||||||
|
{
|
||||||
|
cmdmodel->current_token = token;
|
||||||
|
event_t event = lookup (token);
|
||||||
|
if (!handle_event (cmdmodel, event))
|
||||||
|
break;
|
||||||
|
|
||||||
|
token = strtok (NULL, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free remaining allocated data
|
||||||
|
if (cmdmodel->args != NULL)
|
||||||
|
free (cmdmodel->args);
|
||||||
|
free (cmdmodel);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "effects.h"
|
||||||
|
#include "model.h"
|
||||||
|
|
||||||
|
// TODO: Complete this table mapping state/events to the target state. Each
|
||||||
|
// row corresponds to the current state. Each column within that row indicates
|
||||||
|
// the next state for a given event. If there is no transition defined, that
|
||||||
|
// entry should be NST ("no state").
|
||||||
|
static state_t const _transitions[NUM_STATES][NUM_EVENTS]
|
||||||
|
= { { Command, Term, NST },
|
||||||
|
{ Arguments, Make_Pipe, Term },
|
||||||
|
{ Arguments, Make_Pipe, Term },
|
||||||
|
{ Command, Term, Term },
|
||||||
|
{ NST, NST, NST } };
|
||||||
|
|
||||||
|
// TODO: Create a table mapping states/events to the effect functions. If
|
||||||
|
// there is no valid transition, the entry here would be NULL because actions
|
||||||
|
// are function pointers.
|
||||||
|
static action_t const _effects[NUM_STATES][NUM_EVENTS]
|
||||||
|
= { { start_command, error_pipe, NULL },
|
||||||
|
{ append, link_commands, execute },
|
||||||
|
{ append, link_commands, execute },
|
||||||
|
{ start_command, error_pipe, error_newline },
|
||||||
|
{ NULL, NULL, NULL } };
|
||||||
|
|
||||||
|
/* Create an instance of an FSM and initialize its fields as appropriate.
|
||||||
|
Some fields are common to most FSMs (such as an initial state or a
|
||||||
|
pointer to a transition function). Other fields will be specific to
|
||||||
|
this fsm_t declaration. Return NULL if any part of the initialization
|
||||||
|
fails. */
|
||||||
|
fsm_t *
|
||||||
|
cmdline_init (void)
|
||||||
|
{
|
||||||
|
fsm_t *fsm = calloc (1, sizeof (fsm_t));
|
||||||
|
fsm->state = Init;
|
||||||
|
fsm->transition = transition;
|
||||||
|
|
||||||
|
return fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create a transition function that is specific to this type
|
||||||
|
// of FSM. This function needs to take an fsm_t* and an event, returning
|
||||||
|
// both the new state and the effect to perform (the latter is returned
|
||||||
|
// using a call-by-reference parameter. This function should NOT contain
|
||||||
|
// any "if" types of statements based on the state or event; it should
|
||||||
|
// simply lookup these values in the tables defined above.
|
||||||
|
state_t
|
||||||
|
transition (struct fsm *fsm, event_t event, action_t *effect)
|
||||||
|
{
|
||||||
|
assert (fsm->state < NST);
|
||||||
|
assert (event < NIL);
|
||||||
|
|
||||||
|
*effect = _effects[fsm->state][event];
|
||||||
|
return _transitions[fsm->state][event];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for providing a printable string name for an event */
|
||||||
|
const char *
|
||||||
|
event_name (event_t evt)
|
||||||
|
{
|
||||||
|
assert (evt <= NIL);
|
||||||
|
|
||||||
|
// Event names for printing out
|
||||||
|
const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" };
|
||||||
|
return names[evt];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for providing a printable string name for an state */
|
||||||
|
const char *
|
||||||
|
state_name (state_t st)
|
||||||
|
{
|
||||||
|
assert (st <= NST);
|
||||||
|
|
||||||
|
// State names for printing out
|
||||||
|
const char *names[]
|
||||||
|
= { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" };
|
||||||
|
return names[st];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic front-end for handling events. Should do nothing more
|
||||||
|
than calling the FSM's transition function, performing an effect
|
||||||
|
(if appropriate) and updating the state. Return false if the new
|
||||||
|
state is the terminal state. */
|
||||||
|
bool
|
||||||
|
handle_event (fsm_t *fsm, event_t event)
|
||||||
|
{
|
||||||
|
assert (fsm != NULL);
|
||||||
|
// TODO: Look up the current state/event combination in the
|
||||||
|
// transition table. Print the following line for debugging
|
||||||
|
// purposes just for this lab. This should be printed even
|
||||||
|
// if there is no transition.
|
||||||
|
action_t effect;
|
||||||
|
state_t new_state = fsm->transition (fsm, event, &effect);
|
||||||
|
printf ("[%s.%s -> %s]\n", state_name (fsm->state), event_name (event),
|
||||||
|
state_name (new_state));
|
||||||
|
|
||||||
|
// TODO: If the state/event combination is valid, execute
|
||||||
|
// the transition and effect function (if there is one).
|
||||||
|
// If the next state is Term (terminated), return false.
|
||||||
|
// Otherwise return true.
|
||||||
|
if (new_state != NST)
|
||||||
|
{
|
||||||
|
if (effect != NULL)
|
||||||
|
{
|
||||||
|
effect (fsm);
|
||||||
|
}
|
||||||
|
|
||||||
|
fsm->state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsm->state != Term;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a string, return the event type. Do not modify this function. */
|
||||||
|
event_t
|
||||||
|
lookup (char *token)
|
||||||
|
{
|
||||||
|
if (!strcmp (token, "|"))
|
||||||
|
return PIPE;
|
||||||
|
|
||||||
|
if (!strcmp (token, "NL"))
|
||||||
|
return NEWLINE;
|
||||||
|
|
||||||
|
return TOKEN;
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef __model_h__
|
||||||
|
#define __model_h__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Generic definitions for any type of statemodel
|
||||||
|
|
||||||
|
// States and events should just be integers
|
||||||
|
typedef int state_t;
|
||||||
|
typedef int event_t;
|
||||||
|
|
||||||
|
// Needed for circular typedef. This lets action_t use fsm_t in its parameter
|
||||||
|
// list, while the struct fsm can use action_t as a field.
|
||||||
|
typedef struct fsm fsm_t;
|
||||||
|
|
||||||
|
// All entry, exit, and effect instances use the action type
|
||||||
|
typedef void (*action_t) (fsm_t *);
|
||||||
|
|
||||||
|
// Each FSM instance contains a current state
|
||||||
|
struct fsm
|
||||||
|
{
|
||||||
|
state_t state; // current state
|
||||||
|
|
||||||
|
// pointer to the FSM's transition function
|
||||||
|
state_t (*transition) (struct fsm *, event_t, action_t *);
|
||||||
|
|
||||||
|
// Additional data fields specific to this FSM
|
||||||
|
char *command; // the name of the command to run
|
||||||
|
size_t nargs; // the number of command-line arguments
|
||||||
|
char **args; // the command-line arguments
|
||||||
|
char *current_token; // current token being processed
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generic entry point for handling events
|
||||||
|
bool handle_event (fsm_t *, event_t);
|
||||||
|
|
||||||
|
// Additional definitions specific to an FSM for command line processing
|
||||||
|
|
||||||
|
#define MAX_ARGUMENTS 10
|
||||||
|
|
||||||
|
// Events
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TOKEN, // normal command-line token
|
||||||
|
PIPE, // vertical bar character
|
||||||
|
NEWLINE, // newline at the end of the command
|
||||||
|
NIL // invalid non-event
|
||||||
|
} cmdevt_t;
|
||||||
|
#define NUM_EVENTS NIL
|
||||||
|
|
||||||
|
// States
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
Init, // initial state
|
||||||
|
Command, // establishing the command name
|
||||||
|
Arguments, // building the argument list
|
||||||
|
Make_Pipe, // linking the commands together for a pipe
|
||||||
|
Term, // terminal state (execute program or error)
|
||||||
|
NST // invalid non-state
|
||||||
|
} cmdst_t;
|
||||||
|
#define NUM_STATES NST
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
fsm_t *cmdline_init (void); // initialize the FSM
|
||||||
|
state_t transition (struct fsm *fsm, event_t event, action_t *effect);
|
||||||
|
event_t lookup (char *); // convert an event string to its numeric value
|
||||||
|
|
||||||
|
// Translate event/state numbers to their string equivalent
|
||||||
|
const char *event_name (event_t);
|
||||||
|
const char *state_name (state_t);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#
|
||||||
|
# Simple Test Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This version of the Makefile includes support for building a test suite. The
|
||||||
|
# recommended framework is Check (http://check.sourceforge.net/). To build and
|
||||||
|
# run the test suite, execute the "test" target. The test suite must be located
|
||||||
|
# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as
|
||||||
|
# they do in the main Makefile.
|
||||||
|
#
|
||||||
|
# To change the default build target (which executes when you just type
|
||||||
|
# "make"), change the right-hand side of the definition of the "default"
|
||||||
|
# target.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=../fsm
|
||||||
|
TEST=testsuite
|
||||||
|
MODS=public.o
|
||||||
|
OBJS=../model.o ../effects.o private.o
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
UTESTOUT=utests.txt
|
||||||
|
ITESTOUT=itests.txt
|
||||||
|
SCHECKOUT=style.txt
|
||||||
|
|
||||||
|
default: $(TEST)
|
||||||
|
|
||||||
|
$(EXE):
|
||||||
|
make -C ../
|
||||||
|
|
||||||
|
test: utest itest style
|
||||||
|
@echo "========================================"
|
||||||
|
|
||||||
|
utest: $(EXE) $(TEST)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " UNIT TESTS"
|
||||||
|
@./utests.sh | tee $(UTESTOUT)
|
||||||
|
|
||||||
|
itest: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " INTEGRATION TESTS"
|
||||||
|
@./integration.sh | tee $(ITESTOUT)
|
||||||
|
|
||||||
|
style: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " CODING STYLE CHECK"
|
||||||
|
@./style.sh 2>/dev/null >$(SCHECKOUT)
|
||||||
|
@cat $(SCHECKOUT)
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall --std=c99
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
#CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
#LDFLAGS+=-L/opt/local/lib
|
||||||
|
#LDFLAGS=
|
||||||
|
LIBS+=-lcheck -lm -lpthread
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Linux)
|
||||||
|
LIBS+=-lrt -lsubunit
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(TEST): $(TEST).o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle
|
||||||
|
|
||||||
|
.PHONY: default clean test unittest inttest
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
015ebe95460b1d6fbf79df280ec3ae5fab362ae8
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
42ae8f1bdf52b57ad234791690bf611379fc8249
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ebc846cda2557a05e691ef4d7dc7de4ee312cbd1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2fd3754d04e83c387269e26d81186b290d299f82
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
17611b03d098cb2d095ddac6dc39c08f66514f4e
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
10b932ebcb7ca5600c9b81652547ea3721854698
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
|
[Command.NEWLINE -> Term]
|
||||||
|
Execute ls with arguments { ls, (null) }
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
|
[Command.TOKEN -> Arguments]
|
||||||
|
Appending -a to the argument list
|
||||||
|
[Arguments.TOKEN -> Arguments]
|
||||||
|
Appending -l to the argument list
|
||||||
|
[Arguments.NEWLINE -> Term]
|
||||||
|
Execute ls with arguments { ls, -a, -l, (null) }
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
|
[Command.TOKEN -> Arguments]
|
||||||
|
Appending data to the argument list
|
||||||
|
[Arguments.NEWLINE -> Term]
|
||||||
|
Execute ls with arguments { ls, data, (null) }
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
|
[Command.PIPE -> Make_Pipe]
|
||||||
|
Set up pipe
|
||||||
|
Execute ls with arguments { ls, (null) }
|
||||||
|
[Make_Pipe.TOKEN -> Command]
|
||||||
|
Starting new command: head
|
||||||
|
[Command.NEWLINE -> Term]
|
||||||
|
Execute head with arguments { head, (null) }
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[Init.TOKEN -> Command]
|
||||||
|
Starting new command: ls
|
||||||
|
[Command.TOKEN -> Arguments]
|
||||||
|
Appending -l to the argument list
|
||||||
|
[Arguments.PIPE -> Make_Pipe]
|
||||||
|
Set up pipe
|
||||||
|
Execute ls with arguments { ls, -l, (null) }
|
||||||
|
[Make_Pipe.TOKEN -> Command]
|
||||||
|
Starting new command: head
|
||||||
|
[Command.TOKEN -> Arguments]
|
||||||
|
Appending -n to the argument list
|
||||||
|
[Arguments.TOKEN -> Arguments]
|
||||||
|
Appending 1 to the argument list
|
||||||
|
[Arguments.NEWLINE -> Term]
|
||||||
|
Execute head with arguments { head, -n, 1, (null) }
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
EXE="../shell"
|
||||||
|
|
||||||
|
function run_test {
|
||||||
|
|
||||||
|
# parameters
|
||||||
|
TAG=$1
|
||||||
|
ARGS=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
OUTPUT=outputs/$TAG.txt
|
||||||
|
DIFF=outputs/$TAG.diff
|
||||||
|
EXPECT=expected/$TAG.txt
|
||||||
|
VALGRND=valgrind/$TAG.txt
|
||||||
|
|
||||||
|
# print tag format
|
||||||
|
PTAG=$(printf '%-30s' "$TAG")
|
||||||
|
|
||||||
|
# check for expected text that needs to be fixed
|
||||||
|
if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# compare the file
|
||||||
|
SHAOUT=$(shasum "$EXPECT" | awk '{print $1}')
|
||||||
|
SHAEXP=$(cat "expected/.$TAG.sha")
|
||||||
|
if [ "$SHAOUT" != "$SHAEXP" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run test and compare output to the expected version
|
||||||
|
$EXE "$ARGS" 2>/dev/null >"$OUTPUT"
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
|
||||||
|
# try alternative solution (if it exists)
|
||||||
|
EXPECT=expected/$TAG-2.txt
|
||||||
|
if [ -e "$EXPECT" ]; then
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run valgrind
|
||||||
|
valgrind $EXE $ARGS &>$VALGRND
|
||||||
|
}
|
||||||
|
|
||||||
|
# initialize output folders
|
||||||
|
mkdir -p outputs
|
||||||
|
mkdir -p valgrind
|
||||||
|
rm -f outputs/* valgrind/*
|
||||||
|
|
||||||
|
# run individual tests
|
||||||
|
source itests.include
|
||||||
|
|
||||||
|
# check for memory leaks
|
||||||
|
LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'`
|
||||||
|
if [ -z "$LEAK" ]; then
|
||||||
|
echo "No memory leak found."
|
||||||
|
else
|
||||||
|
echo "Memory leak(s) found. See files listed below for details."
|
||||||
|
grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g'
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# list of integration tests
|
||||||
|
# format: run_test <TAG> <ARGS>
|
||||||
|
# <TAG> used as the root for all filenames (i.e., "expected/$TAG.txt")
|
||||||
|
# <ARGS> command-line arguments to test
|
||||||
|
|
||||||
|
run_test INTEG_command "ls"
|
||||||
|
run_test INTEG_execute "ls NL"
|
||||||
|
run_test INTEG_execute_name "ls data NL"
|
||||||
|
run_test INTEG_execute_flags "ls -a -l NL"
|
||||||
|
run_test INTEG_pipe "ls | head NL"
|
||||||
|
run_test INTEG_pipe_flags "ls -l | head -n 1 NL"
|
||||||
Binary file not shown.
@@ -0,0 +1,53 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
#include "../effects.h"
|
||||||
|
#include "../model.h"
|
||||||
|
|
||||||
|
/* nothing but starting a new cmdline */
|
||||||
|
START_TEST (UNIT_new_cmdline)
|
||||||
|
{
|
||||||
|
printf ("\n======================================\n");
|
||||||
|
printf ("UNIT TEST: new_cmdline\n\n");
|
||||||
|
|
||||||
|
fsm_t *fsm = cmdline_init ();
|
||||||
|
assert (fsm != NULL);
|
||||||
|
|
||||||
|
handle_event (fsm, TOKEN);
|
||||||
|
ck_assert_int_eq (fsm->state, Command);
|
||||||
|
|
||||||
|
free (fsm);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/* a single cmdline from NEW to TRM */
|
||||||
|
START_TEST (UNIT_exec_cmd)
|
||||||
|
{
|
||||||
|
printf ("\n======================================\n");
|
||||||
|
printf ("UNIT TEST: exec_cmd\n\n");
|
||||||
|
|
||||||
|
fsm_t *fsm = cmdline_init ();
|
||||||
|
assert (fsm != NULL);
|
||||||
|
|
||||||
|
handle_event (fsm, TOKEN);
|
||||||
|
ck_assert_int_eq (fsm->state, Command);
|
||||||
|
|
||||||
|
handle_event (fsm, NEWLINE);
|
||||||
|
ck_assert_int_eq (fsm->state, Term);
|
||||||
|
|
||||||
|
free (fsm);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
void public_tests (Suite *s)
|
||||||
|
{
|
||||||
|
TCase *tc_public = tcase_create ("Public");
|
||||||
|
tcase_add_test (tc_public, UNIT_new_cmdline);
|
||||||
|
tcase_add_test (tc_public, UNIT_exec_cmd);
|
||||||
|
suite_add_tcase (s, tc_public);
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STYLE="gnu"
|
||||||
|
|
||||||
|
IGNORE=()
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
function comp_file {
|
||||||
|
|
||||||
|
SRC=$1
|
||||||
|
SRC_NAME=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
FORMAT=ckstyle/${SRC_NAME}.$STYLE
|
||||||
|
DIFF=ckstyle/${SRC_NAME}.diff
|
||||||
|
|
||||||
|
# run clang-format and compare results
|
||||||
|
clang-format --style=$STYLE $source > $FORMAT
|
||||||
|
diff -u $SRC $FORMAT >$DIFF
|
||||||
|
|
||||||
|
PTAG=$(printf '%-30s' "$SRC_NAME")
|
||||||
|
if [ -s $DIFF ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
FAIL=1
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
rm $DIFF
|
||||||
|
fi
|
||||||
|
rm $FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p ckstyle
|
||||||
|
rm -f ckstyle/*
|
||||||
|
|
||||||
|
for source in $(ls ../*.c ../*.h) ; do
|
||||||
|
SKIP=0
|
||||||
|
src=$(basename $source)
|
||||||
|
for ignore in ${IGNORE[*]} ; do
|
||||||
|
if [ "$src" = "$ignore" ] ; then
|
||||||
|
SKIP=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $SKIP = 0 ] ; then
|
||||||
|
comp_file $source $src
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAIL != 0 ] ; then
|
||||||
|
echo "Code that does not adhere to GNU standards will not be accepted."
|
||||||
|
echo "You must fix these files before submission."
|
||||||
|
else
|
||||||
|
echo "Code correctly adheres to required style."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
extern void public_tests (Suite *s);
|
||||||
|
extern void private_tests (Suite *s);
|
||||||
|
|
||||||
|
Suite * test_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("Default");
|
||||||
|
public_tests (s);
|
||||||
|
private_tests (s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_testsuite ()
|
||||||
|
{
|
||||||
|
Suite *s = test_suite ();
|
||||||
|
SRunner *sr = srunner_create (s);
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
srunner_free (sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
run_testsuite ();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
OUTPUT=utests.output
|
||||||
|
|
||||||
|
./testsuite > "$OUTPUT" 2>/dev/null
|
||||||
|
|
||||||
|
if [ ! -s "$OUTPUT" ] ; then
|
||||||
|
echo "UNIT FAIL (testsuite produced no output)"
|
||||||
|
rm -f "$OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$OUTPUT" | awk '/Failures/,0'
|
||||||
|
|
||||||
|
percent=$(cat "$OUTPUT" | grep Failures | cut -d':' -f1)
|
||||||
|
rm -f "$OUTPUT"
|
||||||
|
[ "$percent" = "100%" ] && exit 0
|
||||||
|
|
||||||
|
echo "(run ./testsuite in tests directory for more information)"
|
||||||
|
exit 1
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Ignore all compiled files regardless of location
|
||||||
|
**/*.o
|
||||||
|
|
||||||
|
# But not the private.o
|
||||||
|
!tests/private.o
|
||||||
|
|
||||||
|
# Ignore executables for this project
|
||||||
|
procs
|
||||||
|
testsuite
|
||||||
|
|
||||||
|
# Ignore test outputs
|
||||||
|
tests/ckstyle
|
||||||
|
tests/itests.txt
|
||||||
|
tests/outputs
|
||||||
|
tests/style.txt
|
||||||
|
tests/valgrind
|
||||||
|
tests/utests.txt
|
||||||
|
|
||||||
|
**/.nfs*
|
||||||
|
**/.vscode
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# Simple Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This makefile builds a simple application that contains a main module
|
||||||
|
# (specified by the EXE variable) and a predefined list of additional modules
|
||||||
|
# (specified by the MODS variable). If there are any external library
|
||||||
|
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
|
||||||
|
# If there are any precompiled object files, list them in the OBJS variable.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=procs
|
||||||
|
MODS=signals.o child.o
|
||||||
|
OBJS=
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
default: $(EXE)
|
||||||
|
|
||||||
|
test: $(EXE)
|
||||||
|
make -C tests test
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic -D_POSIX_SOURCE
|
||||||
|
LDFLAGS=-g -O0 -pthread
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(EXE): main.o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXE) main.o $(MODS)
|
||||||
|
make -C tests clean
|
||||||
|
|
||||||
|
.PHONY: default clean
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "child.h"
|
||||||
|
|
||||||
|
pid_t
|
||||||
|
run_child (int rc)
|
||||||
|
{
|
||||||
|
char *str = "I am the parent";
|
||||||
|
|
||||||
|
// TODO: Add code here to create a child process that prints
|
||||||
|
// "I am the child" and exits with return code passed as the rc
|
||||||
|
// parameter to this function. The parent must wait for the child
|
||||||
|
// to finish before printing the child's exit status (consult the
|
||||||
|
// textbook for this as needed) before printing "I am the parent".
|
||||||
|
// The parent should return the child's PID from this function.
|
||||||
|
int status = 0;
|
||||||
|
pid_t pid = fork ();
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
printf ("I am the child\n");
|
||||||
|
exit (rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
waitpid (pid, &status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf ("Child exited with status %d\n", WEXITSTATUS (status));
|
||||||
|
|
||||||
|
printf ("%s\n", str);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __CS361_CHILD_H__
|
||||||
|
#define __CS361_CHILD_H__
|
||||||
|
|
||||||
|
pid_t run_child (int);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* CS 361: Process management lab
|
||||||
|
*
|
||||||
|
* Name:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "child.h"
|
||||||
|
#include "signals.h"
|
||||||
|
|
||||||
|
#define SIGNAL_MAX 20
|
||||||
|
|
||||||
|
int cmdline (int, char **, int *, char **);
|
||||||
|
|
||||||
|
void
|
||||||
|
usage (void)
|
||||||
|
{
|
||||||
|
printf ("Usage: procs [option]\n");
|
||||||
|
printf (" Options are:\n");
|
||||||
|
printf (" -e N Create a child that exits with status N\n");
|
||||||
|
printf (" -s N Override signal number N and run signal handler\n");
|
||||||
|
printf ("\n Default is to create a child process with normal exit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *signal_override = NULL;
|
||||||
|
int rc = 42;
|
||||||
|
if (cmdline (argc, argv, &rc, &signal_override) < 0)
|
||||||
|
{
|
||||||
|
usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a timeout to kill the process after 5 seconds
|
||||||
|
struct itimerval timeout;
|
||||||
|
memset (&timeout, 0, sizeof (timeout));
|
||||||
|
timeout.it_value.tv_sec = 5;
|
||||||
|
timeout.it_value.tv_usec = 0;
|
||||||
|
setitimer (ITIMER_REAL, &timeout, NULL);
|
||||||
|
|
||||||
|
if (signal_override == NULL)
|
||||||
|
run_child (rc);
|
||||||
|
else
|
||||||
|
override (signal_override);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get
|
||||||
|
the portion of the lab to run. */
|
||||||
|
int
|
||||||
|
cmdline (int argc, char **argv, int *rc, char **sig)
|
||||||
|
{
|
||||||
|
int option;
|
||||||
|
|
||||||
|
while ((option = getopt (argc, argv, "e:s:")) != -1)
|
||||||
|
{
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case 'e':
|
||||||
|
*rc = (int)strtol (optarg, NULL, 10);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
*sig = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "signals.h"
|
||||||
|
|
||||||
|
static void catch_loop (int);
|
||||||
|
|
||||||
|
/* Convert the short signal name (NNN part of SIGNNN) to its number.
|
||||||
|
Consult /usr/include/asm/signal.h for the number mappings. For
|
||||||
|
this lab, hard-code the numbers; do not use the numeric constants.
|
||||||
|
Return -1 if the name isn't one you need to look up. */
|
||||||
|
int
|
||||||
|
signal_lookup (char *name)
|
||||||
|
{
|
||||||
|
// TODO: Look up the numbers for INT, SEGV, KILL, and TSTP
|
||||||
|
if (strcmp (name, "INT") == 0)
|
||||||
|
return 2;
|
||||||
|
if (strcmp (name, "SEGV") == 0)
|
||||||
|
return 11;
|
||||||
|
if (strcmp (name, "KILL") == 0)
|
||||||
|
return 9;
|
||||||
|
if (strcmp (name, "TSTP") == 0)
|
||||||
|
return 20;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point for overriding a signal handler in this lab.
|
||||||
|
void
|
||||||
|
override (char *signal)
|
||||||
|
{
|
||||||
|
int sig = signal_lookup (signal);
|
||||||
|
if (sig == SIGKILL)
|
||||||
|
printf ("Received bad signal name: %s\n", signal);
|
||||||
|
else
|
||||||
|
catch_loop (sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic signal handler. This function doesn't use the signal number
|
||||||
|
// in the provided distribution.
|
||||||
|
void
|
||||||
|
sig_handler (int signum __attribute__ ((unused)))
|
||||||
|
{
|
||||||
|
// TODO: Complete this signal handler so that it writes the message
|
||||||
|
// "Breaking out with signal %d\n" with the signal number that was
|
||||||
|
// passed. Note that you cannot use printf() in a signal handler.
|
||||||
|
// You must use write(). After writing the message, exit with return
|
||||||
|
// code 37.
|
||||||
|
//
|
||||||
|
// HINT: If you fail integration tests and get a message that the
|
||||||
|
// binary files are different, you may be writing an extra byte (the
|
||||||
|
// null byte). You can make sure you avoid this by using strlen()
|
||||||
|
// instead of trying to hard-code a length.
|
||||||
|
char buf[128];
|
||||||
|
snprintf (buf, 128, "Breaking out with signal %d\n", signum);
|
||||||
|
write (STDOUT_FILENO, buf, strlen (buf));
|
||||||
|
exit (37);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
catch_loop (int signum)
|
||||||
|
{
|
||||||
|
// sigaction(signum)
|
||||||
|
// TODO: Override the signal handler for the signal number passed.
|
||||||
|
// Then create a child that runs the following code:
|
||||||
|
// prctl (PR_SET_PDEATHSIG, SIGKILL);
|
||||||
|
// printf ("Child has started\n");
|
||||||
|
// fflush (stdout);
|
||||||
|
// while (1) ;
|
||||||
|
|
||||||
|
struct sigaction sa = { 0 };
|
||||||
|
sa.sa_handler = sig_handler;
|
||||||
|
|
||||||
|
sigaction (signum, &sa, NULL);
|
||||||
|
|
||||||
|
pid_t pid = fork ();
|
||||||
|
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
prctl (PR_SET_PDEATHSIG, SIGKILL);
|
||||||
|
printf ("Child has started\n");
|
||||||
|
fflush (stdout);
|
||||||
|
while (1)
|
||||||
|
pause ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// HINT: You will NOT call sig_handler() directly here. Instead,
|
||||||
|
// you must use sigaction() to modify the signal's interrupt handler
|
||||||
|
// so that sig_handler() will be called later when the signal occurs.
|
||||||
|
|
||||||
|
// After starting the child, the parent will call sleep (1) to wait
|
||||||
|
// for 1 second before sending the signal to the child. The parent
|
||||||
|
// must wait for the child to run (and exit), then print
|
||||||
|
// "Child was stopped with status %d\n" with the child process's exit
|
||||||
|
// status code, retrieved with waitpid(). Note that you will need to
|
||||||
|
// call WEXITSTATUS() on the retrieved status code.
|
||||||
|
|
||||||
|
sleep (1);
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
kill (pid, signum);
|
||||||
|
waitpid (pid, &status, 0);
|
||||||
|
printf ("Child was stopped with status %d\n", WEXITSTATUS (status));
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef __CS361_SIGNALS_H__
|
||||||
|
#define __CS361_SIGNALS_H__
|
||||||
|
|
||||||
|
void override (char *);
|
||||||
|
int signal_lookup (char *);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#
|
||||||
|
# Simple Test Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This version of the Makefile includes support for building a test suite. The
|
||||||
|
# recommended framework is Check (http://check.sourceforge.net/). To build and
|
||||||
|
# run the test suite, execute the "test" target. The test suite must be located
|
||||||
|
# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as
|
||||||
|
# they do in the main Makefile.
|
||||||
|
#
|
||||||
|
# To change the default build target (which executes when you just type
|
||||||
|
# "make"), change the right-hand side of the definition of the "default"
|
||||||
|
# target.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=../sigs
|
||||||
|
TEST=testsuite
|
||||||
|
MODS=public.o
|
||||||
|
OBJS=../signals.o ../child.o
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
UTESTOUT=utests.txt
|
||||||
|
ITESTOUT=itests.txt
|
||||||
|
SCHECKOUT=style.txt
|
||||||
|
|
||||||
|
default: $(TEST)
|
||||||
|
|
||||||
|
$(EXE):
|
||||||
|
make -C ../
|
||||||
|
|
||||||
|
test: utest itest style
|
||||||
|
@echo "========================================"
|
||||||
|
|
||||||
|
utest: $(EXE) $(TEST)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " UNIT TESTS"
|
||||||
|
@./$(TEST) 2>/dev/null >$(UTESTOUT)
|
||||||
|
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
|
||||||
|
|
||||||
|
itest: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " INTEGRATION TESTS"
|
||||||
|
@./integration.sh | tee $(ITESTOUT)
|
||||||
|
|
||||||
|
style: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " CODING STYLE CHECK"
|
||||||
|
@./style.sh 2>/dev/null >$(SCHECKOUT)
|
||||||
|
@cat $(SCHECKOUT)
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall --std=c99 -pedantic
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
LDFLAGS+=-L/opt/local/lib
|
||||||
|
LIBS+=-lcheck -lm -lpthread
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Linux)
|
||||||
|
LIBS+=-lrt -lsubunit
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(TEST): $(TEST).o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind
|
||||||
|
|
||||||
|
.PHONY: default clean test unittest inttest
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
6037b06b79a54f57617de151d505444f78a23489
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ebef9658a6b46bd9cfcfeaf732a6d2444fe0b98a
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
145cbab85e3834cf39635245272d5f80e6bd66f2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
b4ec94fbc4feb8f7ce089341bd6c51e775fce9e1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fb52530bd8b6756b4acec57a42391f8b7c9c0e83
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
I am the child
|
||||||
|
Child exited with status 42
|
||||||
|
I am the parent
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Received bad signal name: KILL
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Child has started
|
||||||
|
Breaking out with signal 11
|
||||||
|
Child was stopped with status 37
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Child has started
|
||||||
|
Breaking out with signal 2
|
||||||
|
Child was stopped with status 37
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
I am the child
|
||||||
|
Child exited with status 54
|
||||||
|
I am the parent
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
EXE="../procs"
|
||||||
|
|
||||||
|
function run_test {
|
||||||
|
|
||||||
|
# parameters
|
||||||
|
TAG=$1
|
||||||
|
ARGS=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
OUTPUT=outputs/$TAG.txt
|
||||||
|
DIFF=outputs/$TAG.diff
|
||||||
|
EXPECT=expected/$TAG.txt
|
||||||
|
VALGRND=valgrind/$TAG.txt
|
||||||
|
|
||||||
|
# print tag format
|
||||||
|
PTAG=$(printf '%-30s' "$TAG")
|
||||||
|
|
||||||
|
# check for expected text that needs to be fixed
|
||||||
|
if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# compare the file
|
||||||
|
SHAOUT=$(shasum "$EXPECT" | awk '{print $1}')
|
||||||
|
SHAEXP=$(cat "expected/.$TAG.sha")
|
||||||
|
if [ "$SHAOUT" != "$SHAEXP" ] ; then
|
||||||
|
echo "$PTAG FAIL (expected file is not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run test and compare output to the expected version
|
||||||
|
$EXE $ARGS 2>/dev/null >"$OUTPUT"
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
|
||||||
|
# try alternative solution (if it exists)
|
||||||
|
EXPECT=expected/$TAG-2.txt
|
||||||
|
if [ -e "$EXPECT" ]; then
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run valgrind
|
||||||
|
valgrind $EXE $ARGS &>$VALGRND
|
||||||
|
}
|
||||||
|
|
||||||
|
# initialize output folders
|
||||||
|
mkdir -p outputs
|
||||||
|
mkdir -p valgrind
|
||||||
|
rm -f outputs/* valgrind/*
|
||||||
|
|
||||||
|
# run individual tests
|
||||||
|
source itests.include
|
||||||
|
|
||||||
|
# check for memory leaks
|
||||||
|
LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'`
|
||||||
|
if [ -z "$LEAK" ]; then
|
||||||
|
echo "No memory leak found."
|
||||||
|
else
|
||||||
|
echo "Memory leak(s) found. See files listed below for details."
|
||||||
|
grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g'
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# list of integration tests
|
||||||
|
# format: run_test <TAG> <ARGS>
|
||||||
|
# <TAG> used as the root for all filenames (i.e., "expected/$TAG.txt")
|
||||||
|
# <ARGS> command-line arguments to test
|
||||||
|
|
||||||
|
run_test INTEG_child ""
|
||||||
|
run_test INTEG_status "-e 54"
|
||||||
|
run_test INTEG_sigint "-s INT"
|
||||||
|
run_test INTEG_segfault "-s SEGV"
|
||||||
|
run_test INTEG_kill "-s KILL"
|
||||||
Binary file not shown.
@@ -0,0 +1,46 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
#include "../signals.h"
|
||||||
|
|
||||||
|
START_TEST (UNIT_good_signals)
|
||||||
|
{
|
||||||
|
printf ("\n======================================\n");
|
||||||
|
printf ("UNIT good signals\n");
|
||||||
|
printf ("This test checks for signal name lookups\n");
|
||||||
|
char *names[] = { "INT", "KILL", "SEGV", "TSTP" };
|
||||||
|
int signals[] = { SIGINT, SIGKILL, SIGSEGV, SIGTSTP };
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int sig = signal_lookup (names[i]);
|
||||||
|
ck_assert_int_eq (signals[i], sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST (UNIT_bad_signals)
|
||||||
|
{
|
||||||
|
printf ("\n======================================\n");
|
||||||
|
printf ("UNIT bad signals\n");
|
||||||
|
printf ("This test checks for other signal name lookups\n");
|
||||||
|
char *names[] = { "HUP", "ALRM", "ILL", "KIL" };
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int sig = signal_lookup (names[i]);
|
||||||
|
ck_assert_int_eq (sig, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
void public_tests (Suite *s)
|
||||||
|
{
|
||||||
|
TCase *tc_public = tcase_create ("Public");
|
||||||
|
tcase_add_test (tc_public, UNIT_good_signals);
|
||||||
|
tcase_add_test (tc_public, UNIT_bad_signals);
|
||||||
|
suite_add_tcase (s, tc_public);
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STYLE="gnu"
|
||||||
|
|
||||||
|
IGNORE=()
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
function comp_file {
|
||||||
|
|
||||||
|
SRC=$1
|
||||||
|
SRC_NAME=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
FORMAT=ckstyle/${SRC_NAME}.$STYLE
|
||||||
|
DIFF=ckstyle/${SRC_NAME}.diff
|
||||||
|
|
||||||
|
# run clang-format and compare results
|
||||||
|
clang-format --style=$STYLE $source > $FORMAT
|
||||||
|
diff -u $SRC $FORMAT >$DIFF
|
||||||
|
|
||||||
|
PTAG=$(printf '%-30s' "$SRC_NAME")
|
||||||
|
if [ -s $DIFF ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
FAIL=1
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
rm $DIFF
|
||||||
|
fi
|
||||||
|
rm $FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p ckstyle
|
||||||
|
rm -f ckstyle/*
|
||||||
|
|
||||||
|
for source in $(ls ../*.c ../*.h) ; do
|
||||||
|
SKIP=0
|
||||||
|
src=$(basename $source)
|
||||||
|
for ignore in ${IGNORE[*]} ; do
|
||||||
|
if [ "$src" = "$ignore" ] ; then
|
||||||
|
SKIP=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $SKIP = 0 ] ; then
|
||||||
|
comp_file $source $src
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAIL != 0 ] ; then
|
||||||
|
echo "Code that does not adhere to GNU standards will not be accepted."
|
||||||
|
echo "You must fix these files before submission."
|
||||||
|
else
|
||||||
|
echo "Code correctly adheres to required style."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
extern void public_tests (Suite *s);
|
||||||
|
//extern void private_tests (Suite *s);
|
||||||
|
|
||||||
|
Suite * test_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("Default");
|
||||||
|
public_tests (s);
|
||||||
|
//private_tests (s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_testsuite ()
|
||||||
|
{
|
||||||
|
Suite *s = test_suite ();
|
||||||
|
SRunner *sr = srunner_create (s);
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
srunner_free (sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
run_testsuite ();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Ignore all compiled files regardless of location
|
||||||
|
**/*.o
|
||||||
|
|
||||||
|
# But not the private.o
|
||||||
|
!tests/private.o
|
||||||
|
|
||||||
|
# Ignore executables for this project
|
||||||
|
ipc
|
||||||
|
testsuite
|
||||||
|
|
||||||
|
# Ignore test outputs
|
||||||
|
tests/ckstyle
|
||||||
|
tests/itests.txt
|
||||||
|
tests/outputs
|
||||||
|
tests/style.txt
|
||||||
|
tests/valgrind
|
||||||
|
tests/utests.txt
|
||||||
|
|
||||||
|
**/.nfs*
|
||||||
|
**/.vscode
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#
|
||||||
|
# Simple Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This makefile builds a simple application that contains a main module
|
||||||
|
# (specified by the EXE variable) and a predefined list of additional modules
|
||||||
|
# (specified by the MODS variable). If there are any external library
|
||||||
|
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
|
||||||
|
# If there are any precompiled object files, list them in the OBJS variable.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=ipc
|
||||||
|
MODS=pipe.o
|
||||||
|
OBJS=
|
||||||
|
LIBS=-lm
|
||||||
|
|
||||||
|
default: $(EXE)
|
||||||
|
|
||||||
|
test: $(EXE)
|
||||||
|
make -C tests test
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
#CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic
|
||||||
|
CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic
|
||||||
|
LDFLAGS=-g -O0 -pthread
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(EXE): main.o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXE) main.o $(MODS)
|
||||||
|
make -C tests clean
|
||||||
|
|
||||||
|
.PHONY: default clean
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
hello
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
goodbye
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alohomora
|
||||||
Binary file not shown.
@@ -0,0 +1,87 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "pipe.h"
|
||||||
|
|
||||||
|
int cmdline (int, char **, bool *, char **, char **, size_t *);
|
||||||
|
|
||||||
|
void
|
||||||
|
usage (void)
|
||||||
|
{
|
||||||
|
printf ("Usage: ipc [options] EXEC ARG\n");
|
||||||
|
printf (" Send ARG through a pipe to child process that runs EXEC.\n");
|
||||||
|
printf (" Option is:\n");
|
||||||
|
printf (" -s Use posix_spawn() instead of fork() and exec().\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool use_spawn = false;
|
||||||
|
char *exec = NULL;
|
||||||
|
char *fname = NULL;
|
||||||
|
size_t lineno = -1;
|
||||||
|
|
||||||
|
if (cmdline (argc, argv, &use_spawn, &exec, &fname, &lineno) < 0)
|
||||||
|
{
|
||||||
|
usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use fork/exec or spawn to run the executable exec
|
||||||
|
char *sum = NULL;
|
||||||
|
if (!use_spawn)
|
||||||
|
sum = get_result (exec, fname);
|
||||||
|
else
|
||||||
|
sum = spawn_result (exec, fname);
|
||||||
|
|
||||||
|
if (sum == NULL)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
printf ("%s(%s) = '%s'\n", exec, fname, sum);
|
||||||
|
|
||||||
|
// If you get an error here, it's because sum is not dynamically
|
||||||
|
// allocated correctly.
|
||||||
|
free (sum);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper for parsing the command line arguments. Sets spawn,
|
||||||
|
filename, and lineno as call-by-reference parameters. Return 0
|
||||||
|
on success, -1 otherwise. */
|
||||||
|
int
|
||||||
|
cmdline (int argc, char **argv, bool *spawn, char **exec, char **filename,
|
||||||
|
size_t *lineno)
|
||||||
|
{
|
||||||
|
int option;
|
||||||
|
|
||||||
|
while ((option = getopt (argc, argv, "sh")) != -1)
|
||||||
|
{
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
*spawn = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind + 2 != argc)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*exec = argv[optind];
|
||||||
|
*filename = argv[optind + 1];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
#include <fcntl.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "pipe.h"
|
||||||
|
|
||||||
|
/* Creates a child process. If this fails, close the pipe and return -1.
|
||||||
|
If the child is created, use it to run the executable program on the
|
||||||
|
filename passed. Send the result back through the pipe. The parent process
|
||||||
|
should just return the child's PID. Sample call:
|
||||||
|
|
||||||
|
int fd[2];
|
||||||
|
pipe (fd);
|
||||||
|
pid_t child_pid = create_child (exec, fd, "foo.txt");
|
||||||
|
*/
|
||||||
|
pid_t
|
||||||
|
create_child (char *const exec, int *pipe, char *const filename)
|
||||||
|
{
|
||||||
|
char *args[3] = { exec, filename, NULL };
|
||||||
|
pid_t pid = fork ();
|
||||||
|
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
close (pipe[0]);
|
||||||
|
dup2 (pipe[1], STDOUT_FILENO);
|
||||||
|
close (pipe[1]);
|
||||||
|
execvp (exec, args);
|
||||||
|
exit (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (pipe[1]);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Runs the executable program on the input filename. Start by creating a
|
||||||
|
pipe and calling create_child() to create the child process. If that
|
||||||
|
fails, return NULL. Otherwise, implement the parent here so that it
|
||||||
|
reads the result of the process from the pipe and returns the result
|
||||||
|
without a newline. The returned string must be a dynamically allocated
|
||||||
|
copy; you can use the constant BUFFER_LENGTH for its size. Be sure to
|
||||||
|
close the ends of the pipe at appropriate times. Sample call:
|
||||||
|
|
||||||
|
char *sum = get_result ("cksum", "data/f1.txt");
|
||||||
|
// sum is "3015617425 6 data/f1.txt" [without the newline]
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
get_result (char *const exec, char *const filename)
|
||||||
|
{
|
||||||
|
int fd[2];
|
||||||
|
if (pipe (fd) == -1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = create_child (exec, fd, filename);
|
||||||
|
|
||||||
|
char *buffer = calloc (BUFFER_LENGTH, sizeof (char));
|
||||||
|
ssize_t n = read (fd[0], buffer, BUFFER_LENGTH);
|
||||||
|
buffer[n - 1] = '\0';
|
||||||
|
|
||||||
|
close (fd[0]);
|
||||||
|
waitpid (pid, NULL, 0);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Re-implementation of the create_child() in pipe.c. Instead of
|
||||||
|
using the calls to pipe/fork/dup2/exec, combine the latter three
|
||||||
|
into a call to posix_spawn(). Sample call:
|
||||||
|
|
||||||
|
char *sum = spawn_result ("cksum", "data/f1.txt");
|
||||||
|
// sum is "3015617425 6 data/f1.txt\n" [with the newline]
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
spawn_result (char *const exec, char *const filename)
|
||||||
|
{
|
||||||
|
// Instead of using fork() and exec(), use the posix_spawn functions.
|
||||||
|
// Note that you will need to create a pipe, initialize a
|
||||||
|
// posix_spawn_file_actions_t instance, and add close and dup2 actions
|
||||||
|
// to it so that the child process writes its STDOUT to the pipe.
|
||||||
|
// After spawning the new process, make sure to call
|
||||||
|
// posix_spawn_file_actions_destroy() on the actions to prevent
|
||||||
|
// memory leaks. To avoid looking up the executable in the $PATH,
|
||||||
|
// you can use posix_spawnp().
|
||||||
|
|
||||||
|
// Parent code: read the value back from the pipe into a dynamically
|
||||||
|
// allocated buffer. Wait for the child to exit, then return the
|
||||||
|
// buffer. Make sure to close the ends of the pipe in appropriate
|
||||||
|
// places.
|
||||||
|
int fd[2];
|
||||||
|
pipe (fd);
|
||||||
|
|
||||||
|
posix_spawn_file_actions_t actions;
|
||||||
|
posix_spawn_file_actions_init (&actions);
|
||||||
|
posix_spawn_file_actions_addclose (&actions, fd[0]);
|
||||||
|
posix_spawn_file_actions_adddup2 (&actions, fd[1], STDOUT_FILENO);
|
||||||
|
posix_spawn_file_actions_addclose (&actions, fd[1]);
|
||||||
|
|
||||||
|
pid_t pid = -1;
|
||||||
|
char *args[3] = { exec, filename, NULL };
|
||||||
|
|
||||||
|
posix_spawnp (&pid, exec, &actions, NULL, args, NULL);
|
||||||
|
posix_spawn_file_actions_destroy (&actions);
|
||||||
|
close (fd[1]);
|
||||||
|
|
||||||
|
char *buffer = calloc (BUFFER_LENGTH, sizeof (char));
|
||||||
|
ssize_t n = read (fd[0], buffer, BUFFER_LENGTH);
|
||||||
|
buffer[n - 1] = '\0';
|
||||||
|
|
||||||
|
close (fd[0]);
|
||||||
|
waitpid (pid, NULL, 0);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __pipe_h__
|
||||||
|
#define __pipe_h__
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define BUFFER_LENGTH 100
|
||||||
|
|
||||||
|
pid_t create_child (char *const, int *, char *const);
|
||||||
|
char *get_result (char *const, char *const);
|
||||||
|
char *spawn_result (char *const, char *const);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#
|
||||||
|
# Simple Test Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This version of the Makefile includes support for building a test suite. The
|
||||||
|
# recommended framework is Check (http://check.sourceforge.net/). To build and
|
||||||
|
# run the test suite, execute the "test" target. The test suite must be located
|
||||||
|
# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as
|
||||||
|
# they do in the main Makefile.
|
||||||
|
#
|
||||||
|
# To change the default build target (which executes when you just type
|
||||||
|
# "make"), change the right-hand side of the definition of the "default"
|
||||||
|
# target.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=../ipc
|
||||||
|
TEST=testsuite
|
||||||
|
MODS=public.o
|
||||||
|
OBJS=../pipe.o private.o
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
UTESTOUT=utests.txt
|
||||||
|
ITESTOUT=itests.txt
|
||||||
|
SCHECKOUT=style.txt
|
||||||
|
|
||||||
|
default: $(TEST)
|
||||||
|
|
||||||
|
$(EXE):
|
||||||
|
make -C ../
|
||||||
|
|
||||||
|
test: utest itest style
|
||||||
|
@echo "========================================"
|
||||||
|
|
||||||
|
utest: $(EXE) $(TEST)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " UNIT TESTS"
|
||||||
|
@./$(TEST) 2>/dev/null >$(UTESTOUT)
|
||||||
|
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
|
||||||
|
|
||||||
|
itest: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " INTEGRATION TESTS"
|
||||||
|
@./integration.sh | tee $(ITESTOUT)
|
||||||
|
|
||||||
|
style: $(EXE)
|
||||||
|
@echo "========================================"
|
||||||
|
@echo " CODING STYLE CHECK"
|
||||||
|
@./style.sh 2>/dev/null >$(SCHECKOUT)
|
||||||
|
@cat $(SCHECKOUT)
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic
|
||||||
|
LDFLAGS=-g -O0
|
||||||
|
|
||||||
|
CFLAGS=
|
||||||
|
LIBS+=-lcheck -lm -lpthread
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Linux)
|
||||||
|
LIBS+=-lrt -lsubunit
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(TEST): $(TEST).o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle
|
||||||
|
|
||||||
|
.PHONY: default clean test unittest inttest
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
hello
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
goodbye
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alohomora
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
85cdeaba8a3d4b24f3471d8c91724de8fc9eb6de
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e69f8afeb4d967606c11ee01002df44e6c01b910
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
22cdb52c3ef40e84d4213809c7dcd1747efe8ff0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3881b07e1676a7fbe53089dfe7b3add8ffed7fb0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e69f8afeb4d967606c11ee01002df44e6c01b910
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e609d97c46fa6bb297cb26adc575edf33978ca5a
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cksum(../data/f1.txt) = '3015617425 6 ../data/f1.txt'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
asldkfja(../data/f1.txt) = ''
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cksum(../data/f4.txt) = ''
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
shasum(../data/f2.txt) = 'e7d9b82b45d5833c9dada13f2379e7b66c823434 ../data/f2.txt'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
asldkfja(../data/f1.txt) = ''
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
shasum(../data/f5.txt) = ''
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
EXE="../ipc"
|
||||||
|
|
||||||
|
function run_test {
|
||||||
|
|
||||||
|
# parameters
|
||||||
|
TAG=$1
|
||||||
|
ARGS=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
OUTPUT=outputs/$TAG.txt
|
||||||
|
DIFF=outputs/$TAG.diff
|
||||||
|
EXPECT=expected/$TAG.txt
|
||||||
|
VALGRND=valgrind/$TAG.txt
|
||||||
|
|
||||||
|
# print tag format
|
||||||
|
PTAG=$(printf '%-30s' "$TAG")
|
||||||
|
|
||||||
|
# check for expected text that needs to be fixed
|
||||||
|
if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then
|
||||||
|
echo "$PTAG FAIL ($EXPECT not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# compare the file
|
||||||
|
SHAOUT=$(shasum "$EXPECT" | awk '{print $1}')
|
||||||
|
SHAEXP=$(cat "expected/.$TAG.sha")
|
||||||
|
if [ "$SHAOUT" != "$SHAEXP" ] ; then
|
||||||
|
echo "$PTAG FAIL (expected file is not correct)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run test and compare output to the expected version
|
||||||
|
$EXE $ARGS 2>/dev/null >"$OUTPUT"
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
|
||||||
|
# try alternative solution (if it exists)
|
||||||
|
EXPECT=expected/$TAG-2.txt
|
||||||
|
if [ -e "$EXPECT" ]; then
|
||||||
|
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
|
||||||
|
if [ -s "$DIFF" ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run valgrind
|
||||||
|
valgrind $EXE $ARGS &>$VALGRND
|
||||||
|
}
|
||||||
|
|
||||||
|
# initialize output folders
|
||||||
|
mkdir -p outputs
|
||||||
|
mkdir -p valgrind
|
||||||
|
rm -f outputs/* valgrind/*
|
||||||
|
|
||||||
|
# run individual tests
|
||||||
|
source itests.include
|
||||||
|
|
||||||
|
# check for memory leaks
|
||||||
|
LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'`
|
||||||
|
if [ -z "$LEAK" ]; then
|
||||||
|
echo "No memory leak found."
|
||||||
|
else
|
||||||
|
echo "Memory leak(s) found. See files listed below for details."
|
||||||
|
grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g'
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# list of integration tests
|
||||||
|
# format: run_test <TAG> <ARGS>
|
||||||
|
# <TAG> used as the root for all filenames (i.e., "expected/$TAG.txt")
|
||||||
|
# <ARGS> command-line arguments to test
|
||||||
|
|
||||||
|
run_test INTEG_fork "cksum ../data/f1.txt"
|
||||||
|
run_test INTEG_fork_no_exec "asldkfja ../data/f1.txt"
|
||||||
|
run_test INTEG_fork_no_file "cksum ../data/f4.txt"
|
||||||
|
run_test INTEG_spawn "-s shasum ../data/f2.txt"
|
||||||
|
run_test INTEG_spawn_no_exec "-s asldkfja ../data/f1.txt"
|
||||||
|
run_test INTEG_spawn_no_file "-s shasum ../data/f5.txt"
|
||||||
Binary file not shown.
@@ -0,0 +1,35 @@
|
|||||||
|
#include <check.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../pipe.h"
|
||||||
|
|
||||||
|
// Change this to true if you want to inspect the files produced in
|
||||||
|
// tests/inputs for debugging
|
||||||
|
#define DEBUG false
|
||||||
|
|
||||||
|
START_TEST (UNIT_result)
|
||||||
|
{
|
||||||
|
printf ("\n======================================\n");
|
||||||
|
printf ("UNIT_result\n");
|
||||||
|
printf ("Gets cksum of data/f1.txt\n");
|
||||||
|
|
||||||
|
char *result = get_result ("cksum", "data/f1.txt");
|
||||||
|
ck_assert_str_eq (result, "3015617425 6 data/f1.txt");
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
void public_tests (Suite *s)
|
||||||
|
{
|
||||||
|
TCase *tc_public = tcase_create ("Public");
|
||||||
|
tcase_add_test (tc_public, UNIT_result);
|
||||||
|
suite_add_tcase (s, tc_public);
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
STYLE="gnu"
|
||||||
|
|
||||||
|
IGNORE=()
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
function comp_file {
|
||||||
|
|
||||||
|
SRC=$1
|
||||||
|
SRC_NAME=$2
|
||||||
|
|
||||||
|
# file paths
|
||||||
|
FORMAT=ckstyle/${SRC_NAME}.$STYLE
|
||||||
|
DIFF=ckstyle/${SRC_NAME}.diff
|
||||||
|
|
||||||
|
# run clang-format and compare results
|
||||||
|
clang-format --style=$STYLE $source > $FORMAT
|
||||||
|
diff -u $SRC $FORMAT >$DIFF
|
||||||
|
|
||||||
|
PTAG=$(printf '%-30s' "$SRC_NAME")
|
||||||
|
if [ -s $DIFF ]; then
|
||||||
|
echo "$PTAG FAIL (see $DIFF for details)"
|
||||||
|
FAIL=1
|
||||||
|
else
|
||||||
|
echo "$PTAG pass"
|
||||||
|
rm $DIFF
|
||||||
|
fi
|
||||||
|
rm $FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p ckstyle
|
||||||
|
rm -f ckstyle/*
|
||||||
|
|
||||||
|
for source in $(ls ../*.c ../*.h) ; do
|
||||||
|
SKIP=0
|
||||||
|
src=$(basename $source)
|
||||||
|
for ignore in ${IGNORE[*]} ; do
|
||||||
|
if [ "$src" = "$ignore" ] ; then
|
||||||
|
SKIP=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $SKIP = 0 ] ; then
|
||||||
|
comp_file $source $src
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAIL != 0 ] ; then
|
||||||
|
echo "Code that does not adhere to GNU standards will not be accepted."
|
||||||
|
echo "You must fix these files before submission."
|
||||||
|
else
|
||||||
|
echo "Code correctly adheres to required style."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
extern void public_tests (Suite *s);
|
||||||
|
extern void private_tests (Suite *s);
|
||||||
|
|
||||||
|
Suite * test_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("Default");
|
||||||
|
public_tests (s);
|
||||||
|
private_tests (s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_testsuite ()
|
||||||
|
{
|
||||||
|
Suite *s = test_suite ();
|
||||||
|
SRunner *sr = srunner_create (s);
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
srunner_free (sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
run_testsuite ();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Ignore all compiled files regardless of location
|
||||||
|
**/*.o
|
||||||
|
|
||||||
|
# But not the private.o
|
||||||
|
!tests/private.o
|
||||||
|
|
||||||
|
# Ignore executables for this project
|
||||||
|
net
|
||||||
|
testsuite
|
||||||
|
|
||||||
|
# Ignore test outputs
|
||||||
|
tests/ckstyle
|
||||||
|
tests/itests.txt
|
||||||
|
tests/outputs
|
||||||
|
tests/style.txt
|
||||||
|
tests/valgrind
|
||||||
|
tests/utests.txt
|
||||||
|
|
||||||
|
**/.nfs*
|
||||||
|
**/.vscode
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#
|
||||||
|
# Simple Makefile
|
||||||
|
# Mike Lam, James Madison University, August 2016
|
||||||
|
#
|
||||||
|
# This makefile builds a simple application that contains a main module
|
||||||
|
# (specified by the EXE variable) and a predefined list of additional modules
|
||||||
|
# (specified by the MODS variable). If there are any external library
|
||||||
|
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
|
||||||
|
# If there are any precompiled object files, list them in the OBJS variable.
|
||||||
|
#
|
||||||
|
# By default, this makefile will build the project with debugging symbols and
|
||||||
|
# without optimization. To change this, edit or remove the "-g" and "-O0"
|
||||||
|
# options in CFLAGS and LDFLAGS accordingly.
|
||||||
|
#
|
||||||
|
# By default, this makefile build the application using the GNU C compiler,
|
||||||
|
# adhering to the C99 standard with all warnings enabled.
|
||||||
|
|
||||||
|
|
||||||
|
# application-specific settings and run target
|
||||||
|
|
||||||
|
EXE=net
|
||||||
|
MODS=client.o
|
||||||
|
OBJS=
|
||||||
|
LIBS=-lm
|
||||||
|
|
||||||
|
default: $(EXE)
|
||||||
|
|
||||||
|
test: $(EXE)
|
||||||
|
make -C tests test
|
||||||
|
|
||||||
|
# compiler/linker settings
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic
|
||||||
|
CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic
|
||||||
|
LDFLAGS=-g -O0 -pthread
|
||||||
|
|
||||||
|
|
||||||
|
# build targets
|
||||||
|
|
||||||
|
$(EXE): main.o $(MODS) $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXE) main.o $(MODS)
|
||||||
|
make -C tests clean
|
||||||
|
|
||||||
|
.PHONY: default clean
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user