diff --git a/sh/tests/calculator_test.sh b/sh/tests/calculator_test.sh new file mode 100755 index 000000000..ea946ca78 --- /dev/null +++ b/sh/tests/calculator_test.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# set -euo pipefail +IFS=' +' + +script_dirS=$(cd -P "$(dirname "$BASH_SOURCE")" &>/dev/null && pwd) + +challenge() { + submitted="./calculator.sh $@ + " + expected="./calculator.sh $@ + " + submitted+=$(2>&1 bash "$script_dirS"/student/calculator.sh "$@") + submitted+=" + exit status: $?" + expected+=$(2>&1 bash "$script_dirS"/solutions/calculator.sh "$@") + expected+=" + exit status: $?" + + diff -U 1 <(echo "$submitted") <(echo "$expected") + if [ $? != 0 ] + then + exit 1 + fi +} + +# Check if student uses case statement +if [[ $(cat "$script_dirS"/student/calculator.sh | grep case | wc -l) -eq 0 ]] + then + echo "Error: the use of case statement is mandatory" + exit 1 +fi + +# Valid inputs +challenge "15" "+" "10" +challenge "15" "-" "10" +challenge "15" "/" "10" +challenge "15" "*" "10" + +challenge "3491" "+" "-67" +challenge "3491" "-" "-67" +challenge "3491" "/" "-67" +challenge "3491" "*" "-67" + +challenge "-3491" "+" "-67" +challenge "-3491" "-" "-67" +challenge "-3491" "/" "-67" +challenge "-3491" "*" "-67" + +# Invalid inputs + +challenge +challenge "-3491" "*" "-67" "10" "12" + +challenge "20" "/" "0" +challenge "20" "@" "10" +challenge "10" "*" "67invalid" + +# Test operators functions + +source $script_dirS"/student/calculator.sh" 10 + 10 >/dev/null 2>&1 + +if [ $(do_add 11 14) != 25 ] +then + echo "error in function do_add" + exit 1 +fi + +if [ $(do_sub 11 14) != -3 ] +then + echo "error in function do_sub" + exit 1 +fi + +if [ $(do_mult 3 5) != 15 ] +then + echo "error in function do_mult" + exit 1 +fi + +if [ $(do_divide 50 5) != 10 ] +then + echo "error in function do_divide" + exit 1 +fi diff --git a/sh/tests/solutions/calculator.sh b/sh/tests/solutions/calculator.sh new file mode 100755 index 000000000..191d99d31 --- /dev/null +++ b/sh/tests/solutions/calculator.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# Unofficial Bash Strict Mode +set -euo pipefail +IFS=' +' + +number='^-?[0-9]+$' + +do_add () { + echo $(($1 + $2)) +} + +do_sub () { + echo $(($1 - $2)) +} + +do_mult () { + echo $(($1 * $2)) +} + +do_divide () { + echo $(($1 / $2)) +} + + +if [ $# != 3 ] +then + >&2 echo "Error: expect 3 arguments" + exit 1 +elif ! [[ $1 =~ $number && $3 =~ $number ]] +then + >&2 echo "Error: invalid number" + exit 4 +else + case $2 in + + "+") + echo $(do_add $1 $3) + ;; + + "-") + echo $(do_sub $1 $3) + ;; + + "*") + echo $(do_mult $1 $3) + ;; + + "/") + if [ $3 == 0 ] + then + >&2 echo "Error: division by 0" + exit 2 + fi + echo $(do_divide $1 $3) + ;; + + *) + >&2 echo "Error: invalid operator" + exit 3 + ;; + + esac + +fi diff --git a/subjects/devops/calculator/README.md b/subjects/devops/calculator/README.md new file mode 100644 index 000000000..06660678e --- /dev/null +++ b/subjects/devops/calculator/README.md @@ -0,0 +1,96 @@ +## Calculator + +### Instructions + +In this exercise you will make a script `calculator.sh` that will take 3 arguments, calculate the result and print it on the standard output. + +- The first argument and the third argument will be numbers. +- The second argument will be the operator. + +Each operator should have its own function named as follow: +- `+`: `do_add()`. +- `-`: `do_sub()`. +- `*`: `do_mult()`. +- `/`: `do_divide()`. + +Each function will receive two arguments, the left number and the right number, and return the result of the operation. + +The functions assume that the input is valid, so the input must be checked before calling the functions. + +To choose which function to call you must use the `case` statement. + +> The functions will also be tested individually, so it is important to name each function exactly as above, the behavior of the functions have to match the exercise instructions. + +### Usage + +```console +$ ./calculator.sh 20 "*" 3 +60 +$ ./calculator.sh 20 / 20 +1 +$ ./calculator.sh -1 - 10 +-11 +$ +``` + +### Error handling + +All errors will print a specific message on **stderr** (ending with a newline) and returns a specific non-zero value: +- Wrong number of arguments: `"Error: expect 3 arguments"`, returns `1`. +- Division by 0: `"Error: division by 0"`, exit with `2`. +- Invalid operator: `"Error: invalid operator"`, exit with `3`. +- Invalid number(s): `"Error: invalid number"`, exit with `4`. + +> Negative numbers are also a valid input. + +### Hints + +- `case` statement example: +```sh +# Check the first argument given to a script +case $1 in + "left") + echo "We will turn left" + ;; + + "right") + echo "We will turn right" + ;; + + "top") + echo "We will turn top" + ;; + + "bottom") + echo "We will turn bottom" + ;; + + # Any other case + *) + # This is printed in stderr + >&2 echo "Error: invalid argument" + exit 2 + ;; +esac +``` + +- Example of a function taking two arguments and returning a value by printing it. +The behavior of this function is the same than the one expected for the operators functions you will create: +```sh +print_full_name () { + name=$1 + surname=$2 + echo $name $surname +} + +print_full_name "Gene" "Mallamar" +``` + +> Google and Man will be your friends! + +### References + +- [Bash functions](https://linuxize.com/post/bash-functions/) +- [Test if a variable is a number](https://stackoverflow.com/questions/806906/how-do-i-test-if-a-variable-is-a-number-in-bash) +- [Print on standard error](https://stackoverflow.com/questions/2990414/echo-that-outputs-to-stderr) +- [Case statement](https://linuxize.com/post/bash-case-statement/)