P04 lab code added
This commit is contained in:
		
							parent
							
								
									22d3f0c804
								
							
						
					
					
						commit
						6d88ce4920
					
				| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
 | 
			
		||||
 | 
			
		||||
TARGET     := bin/dep2dot
 | 
			
		||||
# Add all additional c-files to the SOURCES variable
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
SOURCES    := src/main.c
 | 
			
		||||
# END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
TSTSOURCES := tests/tests.c
 | 
			
		||||
 | 
			
		||||
include $(SNP_SHARED_MAKEFILE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# DEPFILES :=  ... define a list of png file names: %.c -> %.c.png
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# define dep target as .PHONEY
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# define dep target depending on FULLTARGET and DEPFILES above
 | 
			
		||||
# action: echo some text telling that the target is done using $@ - the echo command shall not be echoed before execution
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# define new suffix rule for %.png depending on %.dot
 | 
			
		||||
# action: dot -Tpng $< >$@ || $(RM) $@
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# define new suffix rule for %.dot depending on %.dep
 | 
			
		||||
# action: call $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# converts any .c file into a .c.dep file by means of GCC -H switch
 | 
			
		||||
# note: it removes intermediate files which were created as side effect
 | 
			
		||||
%.c.dep: %.c
 | 
			
		||||
	$(COMPILE.c) -H -o $@.x $< 2>$@ && $(RM) $@.x $@.d
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# cleanup all results, including the ones od creating the dependencies
 | 
			
		||||
dep-clean: clean
 | 
			
		||||
	$(RM) $(DEPFILES) $(wildcard src/*.dep src/*.dot)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @mainpage SNP - P04 Modularisation
 | 
			
		||||
 *
 | 
			
		||||
 * @section Purpose
 | 
			
		||||
 *
 | 
			
		||||
 * This is a lab for splitting functionality into multiple modules.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,149 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief  Implementation of the dependency file access.
 | 
			
		||||
 */
 | 
			
		||||
#include "data.h"
 | 
			
		||||
#include "error.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_PATH_LEN     512  ///< @brief  Arbitrarily chosen maximum accepted path lenght.
 | 
			
		||||
#define MAX_LINE_LEN     512  ///< @brief  Arbitrarily chosen maximum accepted line length
 | 
			
		||||
#define MAX_DIRS         64   ///< @brief  Arbitrarily chosen maximum number of supported individual directories per dependency file.
 | 
			
		||||
#define MAX_FILES        256  ///< @brief  Arbitrarily chosen maximum number of supported individual denendency entries.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief          Declaration of POSIX (but not C99) function.
 | 
			
		||||
 * @param  s [IN]  The string to duplicate on the heap memory.
 | 
			
		||||
 * @return         The duplicated string.
 | 
			
		||||
 * @remark         Since the Makefile calls gcc with -std=c99, non-C99 POSIX and GNU extensions are excluded - the glibc, though, provides the function to the linker.
 | 
			
		||||
 */
 | 
			
		||||
char *strdup(const char *s); // not stdc99, but available in the glibc
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief                Initialized the data structure before the data is to be read from th edependency file.
 | 
			
		||||
 * @param  data [INOUT]  The address of the instance to initialize.
 | 
			
		||||
 */
 | 
			
		||||
static void init(data_t *data)
 | 
			
		||||
{
 | 
			
		||||
	assert(data);
 | 
			
		||||
	memset(data, 0, sizeof(data_t));
 | 
			
		||||
	data->dirs = malloc(MAX_DIRS * sizeof(dir_t));
 | 
			
		||||
	if (!data->dirs) FATAL("no memory left");
 | 
			
		||||
	data->files = malloc(MAX_FILES * sizeof(file_t));
 | 
			
		||||
	if (!data->files) FATAL("no memory left");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief                Updates the directory list with the given data.
 | 
			
		||||
 * @param  data [INOUT]  The instance to update.
 | 
			
		||||
 * @param  path [IN]     The file path of a dependency entry as given by the dependency file.
 | 
			
		||||
 * @return               The index of the directory entry (either an existing matching one or a newly added one).
 | 
			
		||||
 * @remark               Extracts the directory part by means of dirname() from the given path and looks up an existing entry or adds a new one.
 | 
			
		||||
 */
 | 
			
		||||
static size_t get_or_add_dir(data_t *data, const char *path)
 | 
			
		||||
{
 | 
			
		||||
	assert(data);
 | 
			
		||||
	assert(path);
 | 
			
		||||
	// The function dirname() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling dirname().
 | 
			
		||||
	// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
 | 
			
		||||
	char *dup = strdup(path);
 | 
			
		||||
	if (!dup) FATAL("no memory left");
 | 
			
		||||
	char *name = strdup(dirname(dup));
 | 
			
		||||
	if (!name) FATAL("no memory left");
 | 
			
		||||
	free(dup);
 | 
			
		||||
 | 
			
		||||
	// search for a matching entry...
 | 
			
		||||
	size_t i = 0;
 | 
			
		||||
	while(i < data->n_dirs && strcmp(data->dirs[i].name, name) != 0) {
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
	if (i >= MAX_DIRS) FATAL("too many directories");
 | 
			
		||||
 | 
			
		||||
	if (i == data->n_dirs) { // no match found: add
 | 
			
		||||
		// handover the allocated name to the owning new directory entry
 | 
			
		||||
		dir_t dir = { .name = name };
 | 
			
		||||
		// append the new directory entry
 | 
			
		||||
		data->dirs[data->n_dirs] = dir;
 | 
			
		||||
		data->n_dirs++;
 | 
			
		||||
	} else {
 | 
			
		||||
		// release the name since match found, and therefore, no need to keep the allocated name anymore
 | 
			
		||||
		free(name);
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief                Add a file entry from the dependency file to the data structure.
 | 
			
		||||
 * @param  data [INOUT]  The data container instance.
 | 
			
		||||
 * @param  path [IN]     The path of one file entry from the dependency file.
 | 
			
		||||
 * @param  level [IN]    The dependency level of the file entry from the dependency file.
 | 
			
		||||
 * @remark               The sequence of entries in the dependency file is relevant - it implies direct dependencies.
 | 
			
		||||
 */
 | 
			
		||||
static void add_file(data_t *data, const char *path, size_t level)
 | 
			
		||||
{
 | 
			
		||||
	assert(data);
 | 
			
		||||
	assert(path);
 | 
			
		||||
	// The function basename() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling basename().
 | 
			
		||||
	// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
 | 
			
		||||
	char *dup = strdup(path);
 | 
			
		||||
	if (!dup) FATAL("no memory left");
 | 
			
		||||
	char *name = strdup(basename(dup));
 | 
			
		||||
	if (!name) FATAL("no memory left");
 | 
			
		||||
	free(dup);
 | 
			
		||||
 | 
			
		||||
	if (data->n_files >= MAX_FILES) FATAL("too many files");
 | 
			
		||||
	// produce a file entry
 | 
			
		||||
	file_t file = { .name = name, .dir = get_or_add_dir(data, path), .level = level };
 | 
			
		||||
	data->files[data->n_files] = file;
 | 
			
		||||
	data->n_files++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief                Processes one dependency line of the dependency file.
 | 
			
		||||
 * @param  data [INOUT]  The data container instance.
 | 
			
		||||
 * @param  line [IN]     The line to parse and store in data.
 | 
			
		||||
 */
 | 
			
		||||
static void process_line(data_t *data, const char line[])
 | 
			
		||||
{
 | 
			
		||||
	assert(data);
 | 
			
		||||
 | 
			
		||||
	size_t len = strlen(line);
 | 
			
		||||
	assert(len > 0);
 | 
			
		||||
	assert(line[0] == '.');
 | 
			
		||||
 | 
			
		||||
	// read level
 | 
			
		||||
	size_t i = strspn(line, ".");
 | 
			
		||||
	size_t level = i;
 | 
			
		||||
	// skip spaces
 | 
			
		||||
	i += strspn(line+i, " \t");
 | 
			
		||||
	// take rest as path and add the file to the records
 | 
			
		||||
	add_file(data, line+i, level);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The public interface. 
 | 
			
		||||
 */
 | 
			
		||||
const data_t data_read_all(const char *root)
 | 
			
		||||
{
 | 
			
		||||
	data_t data;
 | 
			
		||||
	init(&data);
 | 
			
		||||
	// add as first file the root for the given dependencies
 | 
			
		||||
	add_file(&data, root, 0);
 | 
			
		||||
	
 | 
			
		||||
	char line[MAX_LINE_LEN] = { 0 };
 | 
			
		||||
 | 
			
		||||
	// read all stdin line and only process dependency lines (those starting on a '.')
 | 
			
		||||
	clearerr(stdin);
 | 
			
		||||
	while(fgets(line, MAX_LINE_LEN, stdin)) {
 | 
			
		||||
		size_t len = strlen(line);
 | 
			
		||||
		if (len > 0 && line[len-1] == '\n' && line[0] == '.') { // only dependency lines
 | 
			
		||||
			line[len-1] = '\0';
 | 
			
		||||
			process_line(&data, line);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief  Access to the GCC produced dependency data (via gcc -H command line option).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// begin of include guard
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// includes which are needed in this header file
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  Directory container for file entries of the dependency file.
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  File container for the file entries of the dependency file.
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  Overall container for all directories and all files from the dependency file.
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief            Entry function to read the deendency data from stdin.
 | 
			
		||||
 * @param root [IN]  The name of the root file (the deoendency file does not mention the root file, so, it has to be passed from outside).
 | 
			
		||||
 * @return           The container of the read data from stdin. See the documentation on gcc -H for details on the dependencies, etc.
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// end of include guard
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief  Error handling convenience functions.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _ERROR_H_
 | 
			
		||||
#define _ERROR_H_
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  Prints the message to stderr and terminates with EXIT_FAILURE.
 | 
			
		||||
 * @param  MSG [IN]  The "..." *string literal* to emit as error - no format parameters nor variables supported.
 | 
			
		||||
 */
 | 
			
		||||
#define FATAL(MSG) do { fprintf(stderr, "ERROR: %s\n", MSG); exit(EXIT_FAILURE); } while(0)
 | 
			
		||||
 | 
			
		||||
#endif // _ERROR_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
 /* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    Institute of Embedded Systems              -
 | 
			
		||||
 * --   | | | '_ \|  __|  \___ \   Zuercher Hochschule Winterthur             -
 | 
			
		||||
 * --  _| |_| | | | |____ ____) |  (University of Applied Sciences)           -
 | 
			
		||||
 * -- |_____|_| |_|______|_____/   8401 Winterthur, Switzerland               -
 | 
			
		||||
 * ----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief Lab P04 dep2dot
 | 
			
		||||
 */
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "error.h"
 | 
			
		||||
#include "data.h"
 | 
			
		||||
#include "output.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   main function
 | 
			
		||||
 * @param   argc [in] number of entries in argv
 | 
			
		||||
 * @param   argv [in] program name plus command line arguments
 | 
			
		||||
 * @returns returns success if valid date is given, failure otherwise
 | 
			
		||||
 * @remark  Prerequisit to convert the resulting DOT file on the shell: sodo apt install graphviz
 | 
			
		||||
 * @remark  Convert: gcc -H ... file.c ... 2>file.dep ; dep2dot file.c <file.dep >file.dot && dot -Tpng file.dot >file.png
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, const char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	if (argc < 2) FATAL("missing arguments\nusage: dep2dot file.c <file.dep >file.dot   # from gcc -H ... file.c ... 2>file.dep\n");
 | 
			
		||||
	
 | 
			
		||||
	output_dot(data_read_all(argv[1]));
 | 
			
		||||
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief Provides output functions for various file formats.
 | 
			
		||||
 */
 | 
			
		||||
#include "output.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  Writes the node name of the given file.
 | 
			
		||||
 * @param  file [IN]  The file for which to write the node name.
 | 
			
		||||
 * @remark The dependency data contain duplicates of file entries - the node name must be unique for the path and the *basename* of the files.
 | 
			
		||||
 */
 | 
			
		||||
static void print_node(file_t file)
 | 
			
		||||
{
 | 
			
		||||
	printf("\"%s (cluster_c%zd)\"", file.name, file.dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief  Recursively writes the individual direct dependencies for the file given by curr.
 | 
			
		||||
 * @param  files [IN] The array of all files - the sequence is relevant.
 | 
			
		||||
 * @param  len [IN]   The lenght of the files array, i.e. the upper limit for curr values and the subsequent index values.
 | 
			
		||||
 * @param  curr [IN]  The index into files for the current root for dependencies: curr -> x, curr -> y, ...
 | 
			
		||||
 * @return            Returns the index into files for the next file to process (i.e. curr value for the next call to this function).
 | 
			
		||||
 * @remark            For a given *curr* file, all following files are with depth level + 1 are direct include files.
 | 
			
		||||
 * @remark            All files with a higher level are *indirect* include files, thus *direct* includes from files processed by recursive calls.
 | 
			
		||||
 * @remark            The list of direct includes to the *curr* file terminates with a level equal of less the the *curr* one (or when the list is exchausted).
 | 
			
		||||
 */
 | 
			
		||||
static size_t dependencies(file_t files[], size_t len, size_t curr)
 | 
			
		||||
{
 | 
			
		||||
	assert(curr < len);
 | 
			
		||||
	size_t level = files[curr].level;
 | 
			
		||||
	size_t file = curr + 1;
 | 
			
		||||
	while(file < len && files[file].level > level) {
 | 
			
		||||
		if (files[file].level == level + 1) {
 | 
			
		||||
			// Write to stdout "  file -> include;\n" where file and include are the DOT node names of the respective files
 | 
			
		||||
			// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
			file = dependencies(files, len, file);
 | 
			
		||||
		} else {
 | 
			
		||||
			file++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Public interface
 | 
			
		||||
 */
 | 
			
		||||
void output_dot(const data_t data)
 | 
			
		||||
{
 | 
			
		||||
	printf("digraph dep {\n");
 | 
			
		||||
	// nodes
 | 
			
		||||
	printf("  node [shape=box]\n");
 | 
			
		||||
	for (size_t file = 0; file < data.n_files; file++) {
 | 
			
		||||
		// Write to stdout "  file [label=\"name\"];\n" where file is the DOT node name and name is the file name
 | 
			
		||||
		// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
	}
 | 
			
		||||
	// directory clusters
 | 
			
		||||
	for (size_t dir = 0; dir < data.n_dirs; dir++) {
 | 
			
		||||
		printf("  subgraph cluster_c%zd {\n", dir);
 | 
			
		||||
		printf("    label=\"%s\"; %s\n", data.dirs[dir].name, strncmp(data.dirs[dir].name, "/usr/", 5) == 0 ? "style=filled; color=lightgrey;" : "color=black;");
 | 
			
		||||
		for (size_t file = 0; file < data.n_files; file++) {
 | 
			
		||||
			if (data.files[file].dir == dir) {
 | 
			
		||||
				// Write to stdout "    file;\n" where file is the DOT node name
 | 
			
		||||
				// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		printf("  }\n");
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// dependencies
 | 
			
		||||
	size_t curr = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		curr = dependencies(data.files, data.n_files, curr);
 | 
			
		||||
	} while(curr < data.n_files);
 | 
			
		||||
	
 | 
			
		||||
	printf("}\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief  Provides output functions for various file formats.
 | 
			
		||||
 */
 | 
			
		||||
// define proper header file here, with include gaurd, etc.
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
Test File
 | 
			
		||||
. dir1/h1_1
 | 
			
		||||
.. dir1/h1_1_2
 | 
			
		||||
. dir1/h1_2
 | 
			
		||||
. dir2/h2_1
 | 
			
		||||
.. dir1/h1_1
 | 
			
		||||
Done
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
Test File
 | 
			
		||||
Done
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    Institute of Embedded Systems              -
 | 
			
		||||
 * --   | | | '_ \|  __|  \___ \   Zuercher Hochschule Winterthur             -
 | 
			
		||||
 * --  _| |_| | | | |____ ____) |  (University of Applied Sciences)           -
 | 
			
		||||
 * -- |_____|_| |_|______|_____/   8401 Winterthur, Switzerland               -
 | 
			
		||||
 * ----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief Test suite for the given package.
 | 
			
		||||
 */
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <CUnit/Basic.h>
 | 
			
		||||
#include "test_utils.h"
 | 
			
		||||
 | 
			
		||||
#ifndef TARGET // must be given by the make file --> see test target
 | 
			
		||||
#error missing TARGET define
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/// @brief alias for EXIT_SUCCESS
 | 
			
		||||
#define OK   EXIT_SUCCESS
 | 
			
		||||
/// @brief alias for EXIT_FAILURE
 | 
			
		||||
#define FAIL EXIT_FAILURE
 | 
			
		||||
 | 
			
		||||
/// @brief The name of the STDOUT text file.
 | 
			
		||||
#define OUTFILE "stdout.txt"
 | 
			
		||||
/// @brief The name of the STDERR text file.
 | 
			
		||||
#define ERRFILE "stderr.txt"
 | 
			
		||||
 | 
			
		||||
/// @brief test data file
 | 
			
		||||
#define IN_NO_DEP "no_dep.input"
 | 
			
		||||
/// @brief test data file
 | 
			
		||||
#define IN_DEP "dep.input"
 | 
			
		||||
 | 
			
		||||
// setup & cleanup
 | 
			
		||||
static int setup(void)
 | 
			
		||||
{
 | 
			
		||||
	remove_file_if_exists(OUTFILE);
 | 
			
		||||
	remove_file_if_exists(ERRFILE);
 | 
			
		||||
	return 0; // success
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int teardown(void)
 | 
			
		||||
{
 | 
			
		||||
	// Do nothing.
 | 
			
		||||
	// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
 | 
			
		||||
	return 0; // success
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tests
 | 
			
		||||
static void test_fail_no_arg(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_no_dep(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange
 | 
			
		||||
	const char *out_txt[] = {
 | 
			
		||||
		"digraph dep {\n",
 | 
			
		||||
		"  node [shape=box]\n",
 | 
			
		||||
		"  \"root (cluster_c0)\" [label=\"root\"];\n",
 | 
			
		||||
		"  subgraph cluster_c0 {\n",
 | 
			
		||||
		"    label=\".\"; color=black;\n",
 | 
			
		||||
		"    \"root (cluster_c0)\";\n",
 | 
			
		||||
		"  }\n",
 | 
			
		||||
		"}\n",
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	// act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_NO_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
 | 
			
		||||
	// assert
 | 
			
		||||
 | 
			
		||||
	assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_dep(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange
 | 
			
		||||
	const char *out_txt[] = {
 | 
			
		||||
		"digraph dep {\n",
 | 
			
		||||
		"  node [shape=box]\n",
 | 
			
		||||
		"  \"root (cluster_c0)\" [label=\"root\"];\n",
 | 
			
		||||
		"  \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
 | 
			
		||||
		"  \"h1_1_2 (cluster_c1)\" [label=\"h1_1_2\"];\n",
 | 
			
		||||
		"  \"h1_2 (cluster_c1)\" [label=\"h1_2\"];\n",
 | 
			
		||||
		"  \"h2_1 (cluster_c2)\" [label=\"h2_1\"];\n",
 | 
			
		||||
		"  \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
 | 
			
		||||
		"  subgraph cluster_c0 {\n",
 | 
			
		||||
		"    label=\".\"; color=black;\n",
 | 
			
		||||
		"    \"root (cluster_c0)\";\n",
 | 
			
		||||
		"  }\n",
 | 
			
		||||
		"  subgraph cluster_c1 {\n",
 | 
			
		||||
		"    label=\"dir1\"; color=black;\n",
 | 
			
		||||
		"    \"h1_1 (cluster_c1)\";\n",
 | 
			
		||||
		"    \"h1_1_2 (cluster_c1)\";\n",
 | 
			
		||||
		"    \"h1_2 (cluster_c1)\";\n",
 | 
			
		||||
		"    \"h1_1 (cluster_c1)\";\n",
 | 
			
		||||
		"  }\n",
 | 
			
		||||
		"  subgraph cluster_c2 {\n",
 | 
			
		||||
		"    label=\"dir2\"; color=black;\n",
 | 
			
		||||
		"    \"h2_1 (cluster_c2)\";\n",
 | 
			
		||||
		"  }\n",
 | 
			
		||||
		"  \"root (cluster_c0)\" -> \"h1_1 (cluster_c1)\";\n",
 | 
			
		||||
		"  \"h1_1 (cluster_c1)\" -> \"h1_1_2 (cluster_c1)\";\n",
 | 
			
		||||
		"  \"root (cluster_c0)\" -> \"h1_2 (cluster_c1)\";\n",
 | 
			
		||||
		"  \"root (cluster_c0)\" -> \"h2_1 (cluster_c2)\";\n",
 | 
			
		||||
		"  \"h2_1 (cluster_c2)\" -> \"h1_1 (cluster_c1)\";\n",
 | 
			
		||||
		"}\n",
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	// act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
 | 
			
		||||
	// assert
 | 
			
		||||
 | 
			
		||||
	assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Registers and runs the tests.
 | 
			
		||||
 * @returns success (0) or one of the CU_ErrorCode (>0)
 | 
			
		||||
 */
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	// setup, run, teardown
 | 
			
		||||
	TestMainBasic("lab test", setup, teardown
 | 
			
		||||
				  , test_fail_no_arg
 | 
			
		||||
				  , test_no_dep
 | 
			
		||||
				  , test_dep
 | 
			
		||||
				  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue