blob: 307b55183005306d2f4c9e43f27b994f1c1c328a [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* A calculator example used to demonstrate the cmocka testing library. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* If this is being built for a unit test. */
#ifdef UNIT_TESTING
/* Redirect printf to a function in the test application so it's possible to
* test the standard output. */
#ifdef printf
#undef printf
#endif /* printf */
extern int example_test_printf(const char *format, ...);
#define printf example_test_printf
extern void print_message(const char *format, ...);
/* Redirect fprintf to a function in the test application so it's possible to
* test error messages. */
#ifdef fprintf
#undef fprintf
#endif /* fprintf */
#define fprintf example_test_fprintf
extern int example_test_fprintf(FILE * const file, const char *format, ...);
/* Redirect assert to mock_assert() so assertions can be caught by cmocka. */
#ifdef assert
#undef assert
#endif /* assert */
#define assert(expression) \
mock_assert((int)(expression), #expression, __FILE__, __LINE__)
void mock_assert(const int result, const char* expression, const char *file,
const int line);
/* Redirect calloc and free to test_calloc() and test_free() so cmocka can
* check for memory leaks. */
#ifdef calloc
#undef calloc
#endif /* calloc */
#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__)
#ifdef free
#undef free
#endif /* free */
#define free(ptr) _test_free(ptr, __FILE__, __LINE__)
void* _test_calloc(const size_t number_of_elements, const size_t size,
const char* file, const int line);
void _test_free(void* const ptr, const char* file, const int line);
int example_main(int argc, char *argv[]);
/* main is defined in the unit test so redefine name of the the main function
* here. */
#define main example_main
/* All functions in this object need to be exposed to the test application,
* so redefine static to nothing. */
#define static
#endif /* UNIT_TESTING */
/* A binary arithmetic integer operation (add, subtract etc.) */
typedef int (*BinaryOperator)(int a, int b);
/* Structure which maps operator strings to functions. */
typedef struct OperatorFunction {
const char* operator;
BinaryOperator function;
} OperatorFunction;
BinaryOperator find_operator_function_by_string(
const size_t number_of_operator_functions,
const OperatorFunction * const operator_functions,
const char* const operator_string);
int perform_operation(
int number_of_arguments, char *arguments[],
const size_t number_of_operator_functions,
const OperatorFunction * const operator_functions,
int * const number_of_intermediate_values,
int ** const intermediate_values, int * const error_occurred);
static int add(int a, int b);
static int subtract(int a, int b);
static int multiply(int a, int b);
static int divide(int a, int b);
/* Associate operator strings to functions. */
static OperatorFunction operator_function_map[] = {
{"+", add},
{"-", subtract},
{"*", multiply},
{"/", divide},
};
static int add(int a, int b) {
return a + b;
}
static int subtract(int a, int b) {
return a - b;
}
static int multiply(int a, int b) {
return a * b;
}
static int divide(int a, int b) {
assert(b); /* Check for divide by zero. */
return a / b;
}
/* Searches the specified array of operator_functions for the function
* associated with the specified operator_string. This function returns the
* function associated with operator_string if successful, NULL otherwise.
*/
BinaryOperator find_operator_function_by_string(
const size_t number_of_operator_functions,
const OperatorFunction * const operator_functions,
const char* const operator_string) {
size_t i;
assert(!number_of_operator_functions || operator_functions);
assert(operator_string != NULL);
for (i = 0; i < number_of_operator_functions; i++) {
const OperatorFunction *const operator_function =
&operator_functions[i];
if (strcmp(operator_function->operator, operator_string) == 0) {
return operator_function->function;
}
}
return NULL;
}
/* Perform a series of binary arithmetic integer operations with no operator
* precedence.
*
* The input expression is specified by arguments which is an array of
* containing number_of_arguments strings. Operators invoked by the expression
* are specified by the array operator_functions containing
* number_of_operator_functions, OperatorFunction structures. The value of
* each binary operation is stored in a pointer returned to intermediate_values
* which is allocated by malloc().
*
* If successful, this function returns the integer result of the operations.
* If an error occurs while performing the operation error_occurred is set to
* 1, the operation is aborted and 0 is returned.
*/
int perform_operation(
int number_of_arguments, char *arguments[],
const size_t number_of_operator_functions,
const OperatorFunction * const operator_functions,
int * const number_of_intermediate_values,
int ** const intermediate_values, int * const error_occurred) {
char *end_of_integer;
int value;
int i;
assert(!number_of_arguments || arguments);
assert(!number_of_operator_functions || operator_functions);
assert(error_occurred != NULL);
assert(number_of_intermediate_values != NULL);
assert(intermediate_values != NULL);
*error_occurred = 0;
*number_of_intermediate_values = 0;
*intermediate_values = NULL;
if (!number_of_arguments)
return 0;
/* Parse the first value. */
value = (int)strtol(arguments[0], &end_of_integer, 10);
if (end_of_integer == arguments[0]) {
/* If an error occurred while parsing the integer. */
fprintf(stderr, "Unable to parse integer from argument %s\n",
arguments[0]);
*error_occurred = 1;
return 0;
}
/* Allocate an array for the output values. */
*intermediate_values = calloc(((number_of_arguments - 1) / 2),
sizeof(**intermediate_values));
i = 1;
while (i < number_of_arguments) {
int other_value;
const char* const operator_string = arguments[i];
const BinaryOperator function = find_operator_function_by_string(
number_of_operator_functions, operator_functions, operator_string);
int * const intermediate_value =
&((*intermediate_values)[*number_of_intermediate_values]);
(*number_of_intermediate_values) ++;
if (!function) {
fprintf(stderr, "Unknown operator %s, argument %d\n",
operator_string, i);
*error_occurred = 1;
break;
}
i ++;
if (i == number_of_arguments) {
fprintf(stderr, "Binary operator %s missing argument\n",
operator_string);
*error_occurred = 1;
break;
}
other_value = (int)strtol(arguments[i], &end_of_integer, 10);
if (end_of_integer == arguments[i]) {
/* If an error occurred while parsing the integer. */
fprintf(stderr, "Unable to parse integer %s of argument %d\n",
arguments[i], i);
*error_occurred = 1;
break;
}
i ++;
/* Perform the operation and store the intermediate value. */
*intermediate_value = function(value, other_value);
value = *intermediate_value;
}
if (*error_occurred) {
free(*intermediate_values);
*intermediate_values = NULL;
*number_of_intermediate_values = 0;
return 0;
}
return value;
}
int main(int argc, char *argv[]) {
int return_value;
int number_of_intermediate_values;
int *intermediate_values;
/* Peform the operation. */
const int result = perform_operation(
argc - 1, &argv[1],
sizeof(operator_function_map) / sizeof(operator_function_map[0]),
operator_function_map, &number_of_intermediate_values,
&intermediate_values, &return_value);
/* If no errors occurred display the result. */
if (!return_value && argc > 1) {
int i;
int intermediate_value_index = 0;
printf("%s\n", argv[1]);
for (i = 2; i < argc; i += 2) {
assert(intermediate_value_index < number_of_intermediate_values);
printf(" %s %s = %d\n", argv[i], argv[i + 1],
intermediate_values[intermediate_value_index++]);
}
printf("= %d\n", result);
}
if (intermediate_values) {
free(intermediate_values);
}
return return_value;
}