P03 lab code added
This commit is contained in:
		
							parent
							
								
									1872dac7f7
								
							
						
					
					
						commit
						22d3f0c804
					
				| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
 | 
			
		||||
 | 
			
		||||
TARGET     := bin/boundinbox
 | 
			
		||||
SOURCES    := src/main.c
 | 
			
		||||
TSTSOURCES := tests/tests.c
 | 
			
		||||
LIBS       := -lm
 | 
			
		||||
 | 
			
		||||
include $(SNP_SHARED_MAKEFILE)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @mainpage SNP - P03 Calculate bounding box
 | 
			
		||||
 *
 | 
			
		||||
 * @section Purpose
 | 
			
		||||
 *
 | 
			
		||||
 * This is a lab to calculate the bounding box of some triangle and to compare the boxes
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,209 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    Institute of Embedded Systems              -
 | 
			
		||||
 * --   | | | '_ \|  __|  \___ \   Zuercher Hochschule Winterthur             -
 | 
			
		||||
 * --  _| |_| | | | |____ ____) |  (University of Applied Sciences)           -
 | 
			
		||||
 * -- |_____|_| |_|______|_____/   8401 Winterthur, Switzerland               -
 | 
			
		||||
 * ----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief Lab P03 weekday
 | 
			
		||||
 */
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @brief point of two coordinate axes
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/// @brief box with an origin point and a dimension w and h
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/// @brief triangle given by three points a, b, and c
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Compares two double values with a given hard coded tolerance.
 | 
			
		||||
 * @param   [in] a  the first value
 | 
			
		||||
 * @param   [in] b  the second value
 | 
			
		||||
 * @returns 0 if fabs(a-b) <= tolerance, -1 if a < b, 1 otherwise
 | 
			
		||||
 * @remark  the tolerance is expected to be 0.05 (internally hard coded)
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Compares two box parameters for their area (w * h of the box).
 | 
			
		||||
 * @param   [in] a  the first box
 | 
			
		||||
 * @param   [in] b  the second box
 | 
			
		||||
 * @returns the return value from compare_double() when given the areas of both boxes as parameter to compare_double()
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Compares the area of the parameter against 0.
 | 
			
		||||
 * @param   [in] box   the box to check against area 0
 | 
			
		||||
 * @returns compare_double() == 0 with the boxes area and 0 as parameters
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   Calculates the bounding box of a triangle
 | 
			
		||||
 * @param   [in] t   the trinagle for which the baounding box is to be calculated
 | 
			
		||||
 * @returns the bounding box of the triangle
 | 
			
		||||
 * @remark  calculates first the point with the minimum x and y of the triangle's points,
 | 
			
		||||
 *          plus as second point the one with the max coordinates of all points
 | 
			
		||||
 * @remark  the minial point is the origin of the bounding box, the width and hight is the x/y delta of the two points
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// forward declaration of used functions in main()
 | 
			
		||||
static box_t get_match(box_t board, triangle_t t);
 | 
			
		||||
static triangle_t triangle_rotate(triangle_t t, double degree);
 | 
			
		||||
static void write_data(box_t board, triangle_t t, int degree);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, const char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    int x1, x2, x3, y1, y2, y3;
 | 
			
		||||
    if (argc < 2 || sscanf(argv[1], "%d/%d-%d/%d-%d/%d", &x1, &y1, &x2, &y2, &x3, &y3) != 6) {
 | 
			
		||||
        fprintf(stderr, "Usage: %s x1/y1-x2/y2-x3/y3\ne.g. 0/0-100/0-50/50\n", argv[0]);
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    box_t board = { { 0, 0 }, 100, 200 };
 | 
			
		||||
    
 | 
			
		||||
    // rotate the triangle in steps of 1 degree to find the "best position" of the triangle in the board
 | 
			
		||||
    triangle_t t = { { x1, y1 }, { x2, y2 }, { x3, y3 } };
 | 
			
		||||
    int degree_best = -1;
 | 
			
		||||
    box_t best;
 | 
			
		||||
    for(int degree = 0; degree < 360; degree++) {
 | 
			
		||||
        box_t match = get_match(board, triangle_rotate(t, degree));
 | 
			
		||||
        if (!is_zero_area(match)) {
 | 
			
		||||
            if (degree_best == -1 || compare_area(match, best) < 0) {
 | 
			
		||||
                degree_best = degree;
 | 
			
		||||
                best = match;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // write as tabular file
 | 
			
		||||
    write_data(board, t, degree_best);
 | 
			
		||||
    
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/****************** internal functions ********************/
 | 
			
		||||
 | 
			
		||||
// forward declarations
 | 
			
		||||
static point_t point_rotate(point_t p, double degree);
 | 
			
		||||
static point_t point_move(point_t p, double dx, double dy);
 | 
			
		||||
static triangle_t triangle_move(triangle_t t, double dx, double dy);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static box_t get_match(box_t board, triangle_t t)
 | 
			
		||||
{
 | 
			
		||||
    box_t b = triangle_bounding_box(t);
 | 
			
		||||
    if (compare_double(board.w, b.w) < 0) return (box_t){ board.p, 0, 0 };
 | 
			
		||||
    if (compare_double(board.h, b.h) < 0) return (box_t){ board.p, 0, 0 };
 | 
			
		||||
    return (box_t){ board.p, b.w, b.h };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static triangle_t triangle_rotate(triangle_t t, double degree)
 | 
			
		||||
{
 | 
			
		||||
    return (triangle_t){ point_rotate(t.a, degree), point_rotate(t.b, degree), point_rotate(t.c, degree)  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static triangle_t triangle_move(triangle_t t, double dx, double dy)
 | 
			
		||||
{
 | 
			
		||||
    return (triangle_t){ point_move(t.a, dx, dy), point_move(t.b, dx, dy), point_move(t.c, dx, dy)  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static point_t point_rotate(point_t p, double degree)
 | 
			
		||||
{
 | 
			
		||||
    double rad = fmod(degree, 360.0) * acos(-1.0) / 180.0;
 | 
			
		||||
    double s = sin(rad);
 | 
			
		||||
    double c = cos(rad);
 | 
			
		||||
    
 | 
			
		||||
    return (point_t){ c*p.x - s*p.y, s*p.x + c*p.y };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static point_t point_move(point_t p, double dx, double dy)
 | 
			
		||||
{
 | 
			
		||||
    return (point_t) { p.x+dx, p.y+dy };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void write_data(box_t board, triangle_t t, int degree)
 | 
			
		||||
{
 | 
			
		||||
    double border = 10.0;
 | 
			
		||||
    double gap = 2*border;
 | 
			
		||||
 | 
			
		||||
    // move board to origin
 | 
			
		||||
    board.p.x = 0.0;
 | 
			
		||||
    board.p.y = 0.0;
 | 
			
		||||
 | 
			
		||||
    // move original triangle to above the board
 | 
			
		||||
    box_t tbb = triangle_bounding_box(t);
 | 
			
		||||
    t = triangle_move(t, -tbb.p.x, -tbb.p.y + board.h + gap);
 | 
			
		||||
    tbb.p.x = 0.0;
 | 
			
		||||
    tbb.p.y = board.h + gap;
 | 
			
		||||
 | 
			
		||||
    // view box
 | 
			
		||||
    box_t view = { { -border, -border }, fmax(board.w, tbb.w) + 2 * border, board.h + gap + tbb.h + 2 * border };
 | 
			
		||||
    printf("viewbox:%.1f:%.1f:%.1f:%.1f\n", view.p.x, view.p.y, view.w, view.h);
 | 
			
		||||
    
 | 
			
		||||
    printf("rect:%.1f:%.1f:%.1f:%.1f:%s\n", board.p.x, board.p.y, board.w, board.h, "gray");
 | 
			
		||||
    printf("polygon:%.1f:%.1f:%.1f:%.1f:%.1f:%.1f:%s\n",  t.a.x, t.a.y, t.b.x, t.b.y, t.c.x, t.c.y, "green");
 | 
			
		||||
 | 
			
		||||
    // there was a match, show it
 | 
			
		||||
    if (degree >= 0) {
 | 
			
		||||
        triangle_t rotated = triangle_rotate(t, degree);
 | 
			
		||||
        box_t rbb = triangle_bounding_box(rotated);
 | 
			
		||||
        t = triangle_move(rotated, -rbb.p.x, -rbb.p.y); // move to origin
 | 
			
		||||
        printf("polygon:%.1f:%.1f:%.1f:%.1f:%.1f:%.1f:%s\n",  t.a.x, t.a.y, t.b.x, t.b.y, t.c.x, t.c.y, "yellow");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# produces a crude HTML embedded SVG drawing from a tabular file (given on stdin) with columns separated by ":"
 | 
			
		||||
# usage: tab2svg < inbut.txt > output.html
 | 
			
		||||
 | 
			
		||||
awk $* -- '
 | 
			
		||||
BEGIN {
 | 
			
		||||
   FS=":"   # field-separator: which character separats fields in a record (i.e. in a line)
 | 
			
		||||
   print "<!DOCTYPE html>"
 | 
			
		||||
   print "<html>"
 | 
			
		||||
   print "  <head>"
 | 
			
		||||
   print "    <title>SVG Data</title>"
 | 
			
		||||
   print "  </head>"
 | 
			
		||||
   print "  <body>"
 | 
			
		||||
}
 | 
			
		||||
/^viewbox/ {
 | 
			
		||||
   x = $2
 | 
			
		||||
   y = $3
 | 
			
		||||
   w = $4
 | 
			
		||||
   h = $5
 | 
			
		||||
}
 | 
			
		||||
/^rect/ {
 | 
			
		||||
   if (h > 0) { 
 | 
			
		||||
      printf "    <b>board = %dmm x %dmm</b>\n</p>\n", $4, $5
 | 
			
		||||
   	  printf "    <svg width=\"%dmm\" viewbox=\"%.1f %.1f %.1f %.1f\" xmlns=\"http://www.w3.org/2000/svg\">\n", $4, x, y, w, h
 | 
			
		||||
	  h = 0
 | 
			
		||||
   }
 | 
			
		||||
   printf "      <rect fill=\"%s\" stroke=\"black\" x=\"%.1f\" y=\"%.1f\" width=\"%.1f\" height=\"%.1f\"/>\n", $6, $2, $3, $4, $5
 | 
			
		||||
}
 | 
			
		||||
/^polygon/ {
 | 
			
		||||
   printf "      <polygon fill=\"%s\" stroke=\"black\" points=\"%.1f,%.1f %.1f,%.1f %.1f,%.1f\"/>\n", $8, $2, $3, $4, $5, $6, $7
 | 
			
		||||
}
 | 
			
		||||
END {
 | 
			
		||||
  print "    </svg>"
 | 
			
		||||
  print "  </body>"
 | 
			
		||||
  print "</html>"
 | 
			
		||||
}
 | 
			
		||||
'
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    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"
 | 
			
		||||
 | 
			
		||||
// 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_no_match(void)
 | 
			
		||||
{
 | 
			
		||||
    // arrange
 | 
			
		||||
    const char *out_txt[] = {
 | 
			
		||||
        "viewbox:-10.0:-10.0:250.0:290.0\n",
 | 
			
		||||
        "rect:0.0:0.0:100.0:200.0:gray\n",
 | 
			
		||||
        "polygon:0.0:220.0:230.0:220.0:100.0:270.0:green\n",
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // act & assert
 | 
			
		||||
    CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 0/0-230/0-100/50 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
    assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_initial_match(void)
 | 
			
		||||
{
 | 
			
		||||
    // arrange
 | 
			
		||||
    const char *out_txt[] = {
 | 
			
		||||
        "viewbox:-10.0:-10.0:120.0:340.0\n",
 | 
			
		||||
        "rect:0.0:0.0:100.0:200.0:gray\n",
 | 
			
		||||
        "polygon:0.0:220.0:100.0:220.0:100.0:320.0:green\n",
 | 
			
		||||
        "polygon:0.0:0.0:100.0:0.0:100.0:100.0:yellow\n",
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // act & assert
 | 
			
		||||
    CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 0/0-100/0-100/100 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
    assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_tight_match(void)
 | 
			
		||||
{
 | 
			
		||||
    // arrange
 | 
			
		||||
    const char *out_txt[] = {
 | 
			
		||||
        "viewbox:-10.0:-10.0:225.0:290.0\n",
 | 
			
		||||
        "rect:0.0:0.0:100.0:200.0:gray\n",
 | 
			
		||||
        "polygon:0.0:220.0:205.0:220.0:205.0:270.0:green\n",
 | 
			
		||||
        "polygon:94.8:0.0:48.7:199.7:0.0:188.5:yellow\n",
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // act & assert
 | 
			
		||||
    CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 0/0-205/0-205/50 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
    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_no_match
 | 
			
		||||
                  , test_initial_match
 | 
			
		||||
                  , test_tight_match
 | 
			
		||||
                  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
 | 
			
		||||
 | 
			
		||||
TARGET     := bin/weekday
 | 
			
		||||
SOURCES    := src/main.c
 | 
			
		||||
TSTSOURCES := tests/tests.c
 | 
			
		||||
 | 
			
		||||
include $(SNP_SHARED_MAKEFILE)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @mainpage SNP - P03 Calculate the week day
 | 
			
		||||
 *
 | 
			
		||||
 * @section Purpose
 | 
			
		||||
 *
 | 
			
		||||
 * This is a lab to calculate the week day from a given Gregorian date.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
 /* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    Institute of Embedded Systems              -
 | 
			
		||||
 * --   | | | '_ \|  __|  \___ \   Zuercher Hochschule Winterthur             -
 | 
			
		||||
 * --  _| |_| | | | |____ ____) |  (University of Applied Sciences)           -
 | 
			
		||||
 * -- |_____|_| |_|______|_____/   8401 Winterthur, Switzerland               -
 | 
			
		||||
 * ----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * @brief Lab P03 weekday
 | 
			
		||||
 */
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// *** TASK1: typedef enum types for month_t (Jan=1,...Dec} ***
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// *** TASK1: typedef struct for date_t ***
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// *** TASK2: typedef enum weekday_t (Sun=0, Mon, ...Sat) ***
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK1: Checks if the given date is a leap year.
 | 
			
		||||
 * @returns 0 = is not leap year, 1 = is leap year
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK1: Calculates the length of the month given by the data parameter
 | 
			
		||||
 * @returns 28, 29, 30, 31 if a valid month, else 0
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK1: Checks if the given date is in the gregorian date range
 | 
			
		||||
 * @returns 0 = no, 1 = yes
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK1: Checks if the given date is a valid date.
 | 
			
		||||
 * @returns 0 = is not valid date, 1 = is valid date
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK2: calculated from a valid date the weekday
 | 
			
		||||
 * @returns returns a weekday in the range Sun...Sat
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief   TASK2: print weekday as 3-letter abreviated English day name
 | 
			
		||||
 */
 | 
			
		||||
// BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, const char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    // TASK1: parse the mandatory argument into a date_t variable and check if the date is valid
 | 
			
		||||
    // BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    // TASK2: calculate the weekday and print it in this format: "%04d-%02d-%02d is a %s\n"
 | 
			
		||||
    // BEGIN-STUDENTS-TO-ADD-CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // END-STUDENTS-TO-ADD-CODE
 | 
			
		||||
    
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,196 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 * --  _____       ______  _____                                              -
 | 
			
		||||
 * -- |_   _|     |  ____|/ ____|                                             -
 | 
			
		||||
 * --   | |  _ __ | |__  | (___    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"
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct tm now()
 | 
			
		||||
{
 | 
			
		||||
	time_t t = time(NULL);
 | 
			
		||||
	return *localtime(&t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char* weekday_name(int wday)
 | 
			
		||||
{
 | 
			
		||||
	assert(0 <= wday && wday <= 6);
 | 
			
		||||
	static const char* days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
 | 
			
		||||
	return days[wday];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tests
 | 
			
		||||
static void test_task1_fail_no_arg(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task1_fail_not_gregorian(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1291-08-01 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1582-10-14 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1583-10-14 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task1_fail_month_out_of_range(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-13-13 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-00-13 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-12-13 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task1_fail_day_out_of_range(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-01-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-01-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-02-30 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-02-29 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2019-02-29 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2019-02-28 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2019-02-29 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2019-02-28 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task1_fail_leap_year(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1900-02-29 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1900-02-28 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2000-02-30 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2000-02-29 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2001-02-29 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2001-02-28 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2004-02-30 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2004-02-29 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task1_valid_date(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-01-01 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-01-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-02-29 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-03-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-04-30 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-05-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-06-30 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-07-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-08-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-09-30 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-10-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-11-30 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-12-31 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-01-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-02-30 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-03-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-04-31 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-05-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-06-31 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-07-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-08-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-09-31 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-10-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-11-31 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 2020-12-32 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task2_start_gregorian(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange & act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1581-10-14 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1581-10-15 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1582-10-14 >" OUTFILE " 2>" ERRFILE)), FAIL);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1582-10-15 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1583-10-14 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1583-10-15 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
 | 
			
		||||
	const char *out_txt[] = { "1582-10-15 is a Fri\n" };
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " 1582-10-15 >" OUTFILE " 2>" ERRFILE)), OK);
 | 
			
		||||
	assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_task2_today(void)
 | 
			
		||||
{
 | 
			
		||||
	// arrange
 | 
			
		||||
	const size_t SIZE = 1000;
 | 
			
		||||
	char command[SIZE];
 | 
			
		||||
	struct tm t = now();
 | 
			
		||||
	snprintf(command, SIZE, "%s %04d-%02d-%02d 2>%s | tail -1 >%s", XSTR(TARGET), t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, ERRFILE, OUTFILE);
 | 
			
		||||
 | 
			
		||||
	char buffer[SIZE];
 | 
			
		||||
	snprintf(buffer, SIZE, "%04d-%02d-%02d is a %s\n", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, weekday_name(t.tm_wday));
 | 
			
		||||
	const char *out_txt[] = { buffer };
 | 
			
		||||
	const char *err_txt[] = { NULL };
 | 
			
		||||
 | 
			
		||||
	// act & assert
 | 
			
		||||
	CU_ASSERT_EQUAL(WEXITSTATUS(system(command)), OK);
 | 
			
		||||
	assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
 | 
			
		||||
	assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_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_task1_fail_no_arg
 | 
			
		||||
				  , test_task1_fail_not_gregorian
 | 
			
		||||
				  , test_task1_fail_month_out_of_range
 | 
			
		||||
				  , test_task1_fail_day_out_of_range
 | 
			
		||||
				  , test_task1_fail_leap_year
 | 
			
		||||
				  , test_task1_valid_date
 | 
			
		||||
				  , test_task2_start_gregorian
 | 
			
		||||
				  , test_task2_today
 | 
			
		||||
				  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue