From caa2555b1dd238c0f3b864b78e6f0ab4185f9f60 Mon Sep 17 00:00:00 2001 From: ant31 <2t.antoine@gmail.com> Date: Wed, 10 Feb 2016 11:51:39 +0100 Subject: [PATCH] Add complete test integration --- .travis.yml | 140 ++- ansible.cfg | 4 + tests/README.md | 33 + tests/ansible.cfg | 4 + tests/cloud_playbooks/create-aws.yml | 33 + tests/cloud_playbooks/create-gce.yml | 33 + tests/cloud_playbooks/delete-aws.yml | 15 + tests/cloud_playbooks/delete-gce.yml | 24 + tests/local_inventory/host_vars/localhost | 12 + tests/local_inventory/hosts.cfg | 1 + tests/run-tests.sh | 8 + tests/scripts/ansibl8s_test.sh | 52 + tests/shebang-unit | 1146 +++++++++++++++++++++ tests/templates/inventory-gce.j2 | 20 + tests/templates/inventory.ini.j2 | 20 + tests/testcases/010_check-apiserver.yml | 12 + tests/testcases/020_check-create-pod.yml | 13 + tests/testcases/030_check-network.yml | 21 + 18 files changed, 1566 insertions(+), 25 deletions(-) create mode 100644 ansible.cfg create mode 100644 tests/README.md create mode 100644 tests/ansible.cfg create mode 100644 tests/cloud_playbooks/create-aws.yml create mode 100644 tests/cloud_playbooks/create-gce.yml create mode 100644 tests/cloud_playbooks/delete-aws.yml create mode 100644 tests/cloud_playbooks/delete-gce.yml create mode 100644 tests/local_inventory/host_vars/localhost create mode 100644 tests/local_inventory/hosts.cfg create mode 100755 tests/run-tests.sh create mode 100644 tests/scripts/ansibl8s_test.sh create mode 100755 tests/shebang-unit create mode 100644 tests/templates/inventory-gce.j2 create mode 100644 tests/templates/inventory.ini.j2 create mode 100644 tests/testcases/010_check-apiserver.yml create mode 100644 tests/testcases/020_check-create-pod.yml create mode 100644 tests/testcases/030_check-network.yml diff --git a/.travis.yml b/.travis.yml index 578bac4cc..46b08b43f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,38 +1,128 @@ -sudo: required -dist: trusty -language: python -python: "2.7" +sudo: false -addons: - hosts: - - node1 +git: + depth: 5 env: - - SITE=cluster.yml ANSIBLE_VERSION=2.0.0 + global: + GCE_USER=travis + SSH_USER=$GCE_USER + TEST_ID=$TRAVIS_JOB_NUMBER + CONTAINER_ENGINE=docker + PRIVATE_KEY=$GCE_PRIVATE_KEY + ANSIBLE_KEEP_REMOTE_FILES=1 + matrix: + # Debian Jessie + - >- + KUBE_NETWORK_PLUGIN=flannel + CLOUD_IMAGE=debian-8 + CLOUD_REGION=europe-west1-b + - >- + KUBE_NETWORK_PLUGIN=calico + CLOUD_IMAGE=debian-8 + CLOUD_REGION=europe-west1-b -install: + # Centos 7 + - >- + KUBE_NETWORK_PLUGIN=flannel + CLOUD_IMAGE=centos-7-sudo + CLOUD_REGION=us-central1-c + + # - >- + # KUBE_NETWORK_PLUGIN=calico + # CLOUD_IMAGE=centos-7-sudo + # CLOUD_REGION=us-central1-c + + + # # Redhat 7 + # - >- + # KUBE_NETWORK_PLUGIN=flannel + # CLOUD_IMAGE=rhel-7-sudo + # CLOUD_REGION=us-east1-d + # SSH_ARGS='--ssh-extra-args="-t"' + # - >- + # KUBE_NETWORK_PLUGIN=calico + # CLOUD_IMAGE=rhel-7-sudo + # CLOUD_REGION=us-east1-d + + # Ubuntu 14.04 + - >- + KUBE_NETWORK_PLUGIN=flannel + CLOUD_IMAGE=ubuntu-1404-trusty + CLOUD_REGION=europe-west1-c + - >- + KUBE_NETWORK_PLUGIN=calico + CLOUD_IMAGE=ubuntu-1404-trusty + CLOUD_REGION=europe-west1-c + + # Ubuntu 15.10 + - >- + KUBE_NETWORK_PLUGIN=flannel + CLOUD_IMAGE=ubuntu-1510-wily + CLOUD_REGION=us-central1-a + - >- + KUBE_NETWORK_PLUGIN=calico + CLOUD_IMAGE=ubuntu-1510-wily + CLOUD_REGION=us-central1-a + + +matrix: + allow_failures: + - env: KUBE_NETWORK_PLUGIN=flannel CLOUD_IMAGE=centos-7-sudo CLOUD_REGION=us-central1-c + +before_install: # Install Ansible. - - sudo -H pip install ansible==${ANSIBLE_VERSION} - - sudo -H pip install netaddr + - pip install --user boto -U + - pip install --user ansible + - pip install --user netaddr + - pip install --user apache-libcloud cache: - directories: - - $HOME/releases + - directories: - $HOME/.cache/pip + - $HOME/.local before_script: - - export PATH=$PATH:/usr/local/bin + - echo "RUN $TRAVIS_JOB_NUMBER $KUBE_NETWORK_PLUGIN $CONTAINER_ENGINE " + - mkdir -p $HOME/.ssh + - echo $PRIVATE_KEY | base64 -d > $HOME/.ssh/id_rsa + - echo $GCE_PEM_FILE | base64 -d > $HOME/.ssh/gce + - chmod 400 $HOME/.ssh/id_rsa + - chmod 755 $HOME/.local/bin/ansible-playbook + - $HOME/.local/bin/ansible-playbook --version + - cp tests/ansible.cfg . +# - "echo $HOME/.local/bin/ansible-playbook -i inventory.ini -u $SSH_USER -e ansible_ssh_user=$SSH_USER $SSH_ARGS -b --become-user=root -e '{\"cloud_provider\": true}' $LOG_LEVEL -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} setup-kubernetes/cluster.yml" script: - # Check the role/playbook's syntax. - - "sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --syntax-check" - - # Run the role/playbook with ansible-playbook. - - "sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --connection=local" - - # Run the role/playbook again, checking to make sure it's idempotent. - > - sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --connection=local - | tee /dev/stderr | grep -q 'changed=0.*failed=0' - && (echo 'Idempotence test: pass' && exit 0) - || (echo 'Idempotence test: fail' && exit 1) + $HOME/.local/bin/ansible-playbook tests/cloud_playbooks/create-gce.yml -i tests/local_inventory/hosts -c local $LOG_LEVEL + -e test_id=${TEST_ID} + -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} + -e gce_project_id=${GCE_PROJECT_ID} + -e gce_service_account_email=${GCE_ACCOUNT} + -e gce_pem_file=${HOME}/.ssh/gce + -e cloud_image=${CLOUD_IMAGE} + -e inventory_path=${PWD}/inventory/inventory.ini + -e cloud_region=${CLOUD_REGION} + + # Create cluster + - "$HOME/.local/bin/ansible-playbook -i inventory/inventory.ini -u $SSH_USER -e ansible_ssh_user=$SSH_USER $SSH_ARGS -b --become-user=root -e '{\"cloud_provider\": true}' $LOG_LEVEL -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} cluster.yml" + # Tests Cases + ## Test Master API + - $HOME/.local/bin/ansible-playbook -i inventory/inventory.ini tests/testcases/010_check-apiserver.yml $LOG_LEVEL + ## Create a POD + - $HOME/.local/bin/ansible-playbook -i inventory/inventory.ini -u $SSH_USER -e ansible_ssh_user=$SSH_USER $SSH_ARGS -b --become-user=root tests/testcases/020_check-create-pod.yml $LOG_LEVEL + ## Ping the between 2 pod + - $HOME/.local/bin/ansible-playbook -i setup-kubernetes/inventory/inventory.ini -u $SSH_USER -e ansible_ssh_user=$SSH_USER $SSH_ARGS -b --become-user=root tests/testcases/030_check-network.yml $LOG_LEVEL + +after_script: + - > + $HOME/.local/bin/ansible-playbook -i inventory/inventory.ini tests/cloud_playbooks/delete-gce.yml -c local $LOG_LEVEL + -e test_id=${TEST_ID} + -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} + -e gce_project_id=${GCE_PROJECT_ID} + -e gce_service_account_email=${GCE_ACCOUNT} + -e gce_pem_file=${HOME}/.ssh/gce + -e cloud_image=${CLOUD_IMAGE} + -e inventory_path=${PWD}/inventory/inventory.ini + -e cloud_region=${CLOUD_REGION} diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 000000000..2be6f4d02 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[ssh_connection] +pipelining=True +[defaults] +host_key_checking=False diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..24b98197f --- /dev/null +++ b/tests/README.md @@ -0,0 +1,33 @@ +# k8s-integration-tests + +*Work In Progress* + +## Test environment variables + +### Common + +Variable | Description | Required | Default +--------------------- | -------------------------------------- | ---------- | -------- +`TEST_ID` | A unique execution ID for this test | Yes | +`KUBE_NETWORK_PLUGIN` | The network plugin (calico or flannel) | Yes | +`PRIVATE_KEY_FILE` | The path to the SSH private key file | No | + +### AWS Tests + +Variable | Description | Required | Default +--------------------- | ----------------------------------------------- | ---------- | --------- +`AWS_ACCESS_KEY` | The Amazon Access Key ID | Yes | +`AWS_SECRET_KEY` | The Amazon Secret Access Key | Yes | +`AWS_AMI_ID` | The AMI ID to deploy | Yes | +`AWS_KEY_PAIR_NAME` | The name of the EC2 key pair to use | Yes | +`AWS_SECURITY_GROUP` | The EC2 Security Group to use | No | default +`AWS_REGION` | The EC2 region | No | eu-central-1 + +#### Use private ssh key + +##### Key + +```bash +openssl pkcs12 -in gce-secure.p12 -passin pass:notasecret -nodes -nocerts | openssl rsa -out gce-secure.pem +cat gce-secure.pem |base64 -w0 > GCE_PEM_FILE` +``` diff --git a/tests/ansible.cfg b/tests/ansible.cfg new file mode 100644 index 000000000..2be6f4d02 --- /dev/null +++ b/tests/ansible.cfg @@ -0,0 +1,4 @@ +[ssh_connection] +pipelining=True +[defaults] +host_key_checking=False diff --git a/tests/cloud_playbooks/create-aws.yml b/tests/cloud_playbooks/create-aws.yml new file mode 100644 index 000000000..b0749ab5b --- /dev/null +++ b/tests/cloud_playbooks/create-aws.yml @@ -0,0 +1,33 @@ +--- +- hosts: localhost + sudo: False + gather_facts: False + + tasks: + - name: Provision a set of instances + ec2: + key_name: "{{ aws.key_name }}" + aws_access_key: "{{ aws.access_key }}" + aws_secret_key: "{{ aws.secret_key }}" + region: "{{ aws.region }}" + group_id: "{{ aws.group}}" + instance_type: "{{ aws.instance_type}}" + image: "{{ aws.ami_id }}" + wait: true + count: "{{ aws.count }}" + instance_tags: "{{ aws.tags }}" + register: ec2 + + - name: Template the inventory + template: + src: templates/inventory.ini.j2 + dest: "{{ inventory_path }}" + + - name: Wait until SSH is available + local_action: + module: wait_for + host: "{{ item.public_ip }}" + port: 22 + timeout: 300 + state: started + with_items: ec2.instances diff --git a/tests/cloud_playbooks/create-gce.yml b/tests/cloud_playbooks/create-gce.yml new file mode 100644 index 000000000..18b5f51a3 --- /dev/null +++ b/tests/cloud_playbooks/create-gce.yml @@ -0,0 +1,33 @@ +--- +- hosts: localhost + sudo: False + gather_facts: no + vars: + cloud_machine_type: g1-small + + tasks: + - name: replace_test_id + set_fact: + test_name: "{{test_id |regex_replace('\\.', '-')}}" + + - name: Create gce instances + gce: + instance_names: "k8s-{{test_name}}-1,k8s-{{test_name}}-2,k8s-{{test_name}}-3" + machine_type: "{{ cloud_machine_type }}" + image: "{{ cloud_image }}" + service_account_email: "{{ gce_service_account_email }}" + pem_file: "{{ gce_pem_file }}" + project_id: "{{ gce_project_id }}" + zone: "{{cloud_region}}" + metadata: '{"test_id": "{{test_id}}", "network": "{{kube_network_plugin}}"}' + tags: "build-{{test_name}},{{kube_network_plugin}}" + register: gce + + - name: Template the inventory + template: + src: ../templates/inventory-gce.j2 + dest: "{{ inventory_path }}" + + - name: Wait for SSH to come up + wait_for: host={{item.public_ip}} port=22 delay=10 timeout=180 state=started + with_items: gce.instance_data diff --git a/tests/cloud_playbooks/delete-aws.yml b/tests/cloud_playbooks/delete-aws.yml new file mode 100644 index 000000000..3b44f3091 --- /dev/null +++ b/tests/cloud_playbooks/delete-aws.yml @@ -0,0 +1,15 @@ +--- +- hosts: kube-node + sudo: False + + tasks: + - name: Gather EC2 facts + action: ec2_facts + + - name: Terminate EC2 instances + local_action: + module: ec2 + state: absent + instance_ids: "{{ ansible_ec2_instance_id }}" + region: "{{ ansible_ec2_placement_region }}" + wait: True diff --git a/tests/cloud_playbooks/delete-gce.yml b/tests/cloud_playbooks/delete-gce.yml new file mode 100644 index 000000000..d42c6cc91 --- /dev/null +++ b/tests/cloud_playbooks/delete-gce.yml @@ -0,0 +1,24 @@ +--- +- hosts: localhost + sudo: False + gather_facts: no + vars: + cloud_machine_type: f1-micro + + tasks: + - name: replace_test_id + set_fact: + test_name: "{{test_id |regex_replace('\\.', '-')}}" + + - name: delete gce instances + gce: + instance_names: "k8s-{{test_name}}-1,k8s-{{test_name}}-2,k8s-{{test_name}}-3" + machine_type: "{{ cloud_machine_type }}" + image: "{{ cloud_image }}" + service_account_email: "{{ gce_service_account_email }}" + pem_file: "{{ gce_pem_file }}" + project_id: "{{ gce_project_id }}" + zone: "{{cloud_region | default('europe-west1-b')}}" + metadata: '{"test_id": "{{test_id}}", "network": "{{kube_network_plugin}}"}' + state: 'absent' + register: gce diff --git a/tests/local_inventory/host_vars/localhost b/tests/local_inventory/host_vars/localhost new file mode 100644 index 000000000..695c0ecbf --- /dev/null +++ b/tests/local_inventory/host_vars/localhost @@ -0,0 +1,12 @@ +aws: + key_name: "{{ key_name | default('ansibl8s') }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region | default('eu-west-1') }}" # default to eu-west-1 + group: "{{ aws_security_group | default ('default')}}" + instance_type: t2.micro + ami_id: "{{ aws_ami_id | default('ami-02724d1f') }}" # default to Debian Jessie + count: 3 + tags: + test_id: "{{ test_id }}" + network_plugin: "{{ kube_network_plugin }}" diff --git a/tests/local_inventory/hosts.cfg b/tests/local_inventory/hosts.cfg new file mode 100644 index 000000000..a3550c31b --- /dev/null +++ b/tests/local_inventory/hosts.cfg @@ -0,0 +1 @@ +localhost ansible_connection=localx \ No newline at end of file diff --git a/tests/run-tests.sh b/tests/run-tests.sh new file mode 100755 index 000000000..c20438e10 --- /dev/null +++ b/tests/run-tests.sh @@ -0,0 +1,8 @@ +#! /bin/bash + +# curl -# -C - -o shebang-unit https://raw.github.com/arpinum-oss/shebang-unit/master/releases/shebang-unit +# chmod +x shebang-unit + +now=$(date +"%Y%m%d%H%M%S") +mkdir -p ${PWD}/tests-results +./shebang-unit --reporters=simple,junit --output-file=${PWD}/tests-results/junit_report-${now}.xml tests diff --git a/tests/scripts/ansibl8s_test.sh b/tests/scripts/ansibl8s_test.sh new file mode 100644 index 000000000..53f8f2d7d --- /dev/null +++ b/tests/scripts/ansibl8s_test.sh @@ -0,0 +1,52 @@ +#! /bin/bash + +global_setup() { + git clone https://github.com/ansibl8s/setup-kubernetes.git setup-kubernetes + private_key="" + if [ ! -z ${PRIVATE_KEY_FILE} ] + then + private_key="--private-key=${PRIVATE_KEY_FILE}" + fi + ansible-playbook create.yml -i hosts -u admin -s \ + -e test_id=${TEST_ID} \ + -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} \ + -e aws_access_key=${AWS_ACCESS_KEY} \ + -e aws_secret_key=${AWS_SECRET_KEY} \ + -e aws_ami_id=${AWS_AMI_ID} \ + -e aws_security_group=${AWS_SECURITY_GROUP} \ + -e key_name=${AWS_KEY_PAIR_NAME} \ + -e inventory_path=${PWD}/inventory.ini \ + -e aws_region=${AWS_REGION} +} + +global_teardown() { + if [ -f inventory.ini ]; + then + ansible-playbook -i inventory.ini -u admin delete.yml + fi + rm -rf ${PWD}/setup-kubernetes +} + +should_deploy_cluster() { + ansible-playbook -i inventory.ini -s ${private_key} -e kube_network_plugin=${KUBE_NETWORK_PLUGIN} setup-kubernetes/cluster.yml + + assertion__status_code_is_success $? +} + +should_api_server_respond() { + ansible-playbook -i inventory.ini ${private_key} testcases/check-apiserver.yml + + assertion__status_code_is_success $? +} + +should_create_pod() { + ansible-playbook -i inventory.ini -s ${private_key} testcases/check-create-pod.yml -vv + + assertion__status_code_is_success $? +} + +should_pod_be_in_expected_subnet() { + ansible-playbook -i inventory.ini -s ${private_key} testcases/check-network.yml -vv + + assertion__status_code_is_success $? +} diff --git a/tests/shebang-unit b/tests/shebang-unit new file mode 100755 index 000000000..6f9a2bc6c --- /dev/null +++ b/tests/shebang-unit @@ -0,0 +1,1146 @@ +#!/usr/bin/env bash + +# Copyright (C) 2015, Arpinum +# +# shebang-unit is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# shebang-unit is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# shebang-unit. If not, see http://www.gnu.org/licenses/lgpl.html. + +# shebang-unit all in one source file + + +configuration__load() { + # yes/no representation used with shebang-unit parameters to activate + # stuff like colors + SBU_YES="yes" + SBU_NO="no" + + # Colors for outputs + SBU_GREEN_COLOR_CODE="\\033[1;32m" + SBU_RED_COLOR_CODE="\\033[1;31m" + SBU_YELLOW_COLOR_CODE="\\033[1;33m" + SBU_DEFAULT_COLOR_CODE="\\e[0m" + + # Functions coding coventions + SBU_GLOBAL_SETUP_FUNCTION_NAME="global_setup" + SBU_GLOBAL_TEARDOWN_FUNCTION_NAME="global_teardown" + SBU_SETUP_FUNCTION_NAME="setup" + SBU_TEARDOWN_FUNCTION_NAME="teardown" + SBU_FUNCTION_DECLARATION_REGEX="^[ ]*\(function\)\{0,1\}[ ]*\([A-Za-z0-9_-]\{1,\}\)[ ]*\(([ ]*)\)\{0,1\}[ ]*{" + SBU_PRIVATE_FUNCTION_NAME_REGEX="^_.*" + + # Default configuration that can be modified with shebang-unit parameters + # For more information see shebang-unit usages + SBU_TEST_FILE_PATTERN="*_test.sh" + SBU_TEST_FUNCTION_PATTERN="*" + SBU_USE_COLORS="${SBU_YES}" + SBU_RANDOM_RUN="${SBU_NO}" + SBU_REPORTERS="simple" + SBU_JUNIT_REPORTER_OUTPUT_FILE="./junit_report.xml" + + # Internal constants + SBU_SUCCESS_STATUS_CODE=0 + SBU_FAILURE_STATUS_CODE=1 + SBU_VALUE_SEPARATOR="," + SBU_TEMP_DIR="/tmp/.shebang-unit" + SBU_LAST_ASSERTION_MSG_KEY="last_assertion_message" + SBU_NO_RUN="${SBU_NO}" + SBU_STANDARD_FD=42 + SBU_ERROR_FD=43 +} + + +assertion__equal() { + if [[ "$1" != "$2" ]]; then + _assertion__failed "Actual: <$2>, expected: <$1>." + fi +} + +assertion__different() { + if [[ "$1" == "$2" ]]; then + _assertion__failed "Both values are: <$1>." + fi +} + +assertion__string_contains() { + if ! system__string_contains "$1" "$2"; then + _assertion__failed "String: <$1> does not contain: <$2>." + fi +} + +assertion__string_does_not_contain() { + if system__string_contains "$1" "$2"; then + _assertion__failed "String: <$1> contains: <$2>." + fi +} + +assertion__string_empty() { + if [[ -n "$1" ]]; then + _assertion__failed "String: <$1> is not empty." + fi +} + +assertion__string_not_empty() { + if [[ -z "$1" ]]; then + _assertion__failed "The string is empty." + fi +} + +assertion__array_contains() { + local element=$1 + shift 1 + if ! array__contains "${element}" "$@"; then + local array_as_string="$(system__pretty_print_array "$@")" + _assertion__failed \ + "Array: <${array_as_string}> does not contain: <${element}>." + fi +} + +assertion__array_does_not_contain() { + local element=$1 + shift 1 + if array__contains "${element}" "$@"; then + local array_as_string="$(system__pretty_print_array "$@")" + _assertion__failed \ + "Array: <${array_as_string}> contains: <${element}>." + fi +} + +assertion__status_code_is_success() { + if (( $1 != ${SBU_SUCCESS_STATUS_CODE} )); then + _assertion__failed \ + "Status code is failure instead of success." "$2" + fi +} + +assertion__status_code_is_failure() { + if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then + _assertion__failed \ + "Status code is success instead of failure." "$2" + fi +} + +assertion__successful() { + "$@" + if (( $? != ${SBU_SUCCESS_STATUS_CODE} )); then + _assertion__failed "Command is failing instead of successful." + fi +} + +assertion__failing() { + "$@" + if (( $? == ${SBU_SUCCESS_STATUS_CODE} )); then + _assertion__failed "Command is successful instead of failing." + fi +} + +_assertion__failed() { + local message_to_use="$(_assertion__get_assertion_message_to_use "$1" "$2")" + system__print_line "Assertion failed. ${message_to_use}" + exit ${SBU_FAILURE_STATUS_CODE} +} + +_assertion__get_assertion_message_to_use() { + local message=$1 + local custom_messsage=$2 + if [[ -n "${custom_messsage}" ]]; then + system__print "${message} ${custom_messsage}" + else + system__print "${message}" + fi +} + + +mock__make_function_do_nothing() { + mock__make_function_call "$1" ":" +} + +mock__make_function_prints() { + local function=$1 + local text=$2 + eval "${function}() { printf "${text}"; }" +} + +mock__make_function_call() { + local function_to_mock=$1 + local function_to_call=$2 + shift 2 + eval "${function_to_mock}() { ${function_to_call} \"\$@\"; }" +} + + +runner__run_all_test_files() { + SBU_BASE_TEST_DIRECTORY=$1 + reporter__test_files_start_running + timer__store_current_time "global_time" + results__test_files_start_running + _runner__run_all_test_files_with_pattern_in_directory "$1" + reporter__test_files_end_running "$(timer__get_time_elapsed "global_time")" + runner__tests_are_successful +} + +_runner__run_all_test_files_with_pattern_in_directory() { + local file + local files + array__from_lines files <<< "$(_runner__get_test_files_in_directory "$1")" + for file in "${files[@]}"; do + file_runner__run_test_file "${file}" + done +} + +_runner__get_test_files_in_directory() { + local files + array__from_lines files <<< "$(find "$1" -name "${SBU_TEST_FILE_PATTERN}" | sort)" + if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then + array__from_lines files <<< "$(system__randomize_array "${files[@]}")" + fi + array__print "${files[@]}" +} + +runner__tests_are_successful() { + (( $(results__get_failing_tests_count) == 0 \ + && $(results__get_skipped_tests_count) == 0 )) +} + + +file_runner__run_test_file() { + local file=$1 + local public_functions=($(parser__get_public_functions_in_file "${file}")) + local test_functions=($(_file_runner__get_test_functions)) + reporter__test_file_starts_running "${file}" "${#test_functions[@]}" + ( source "${file}" + _file_runner__run_global_setup_if_exists \ + && _file_runner__call_all_tests + _file_runner__run_global_teardown_if_exists ) + _file_runner__check_if_global_setup_has_exited + reporter__test_file_ends_running +} + +_file_runner__run_all_tests_if_global_setup_is_successful() { + _file_runner__call_all_tests +} + +_file_runner__call_all_tests() { + local i + for (( i=0; i < ${#test_functions[@]}; i++ )); do + test_runner__run_test "${test_functions[${i}]}" "${public_functions[@]}" + done +} + +_file_runner__skip_all_tests() { + local i + for (( i=0; i < ${#test_functions[@]}; i++ )); do + test_runner__skip_test "${test_functions[${i}]}" "${public_functions[@]}" + done +} + +_file_runner__get_test_functions() { + local result=() + local test_function + for test_function in "${public_functions[@]}"; do + if _file_runner__function_is_a_test "${test_function}"\ + && [[ "${test_function}" == ${SBU_TEST_FUNCTION_PATTERN} ]]; then + result+=("${test_function}") + fi + done + _file_runner__get_randomized_test_functions_if_needed "${result[@]}" +} + +_file_runner__get_randomized_test_functions_if_needed() { + if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then + system__randomize_array "$@" + else + array__print "$@" + fi +} + +_file_runner__run_global_setup_if_exists() { + database__put "sbu_current_global_setup_has_failed" "${SBU_YES}" + _file_runner__call_function_if_exists "${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \ + && database__put "sbu_current_global_setup_has_failed" "${SBU_NO}" +} + +_file_runner__run_global_teardown_if_exists() { + _file_runner__call_function_if_exists "${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}" +} + +_file_runner__function_is_a_test() { + ! array__contains "$1" \ + "${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \ + "${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}" \ + "${SBU_SETUP_FUNCTION_NAME}" \ + "${SBU_TEARDOWN_FUNCTION_NAME}" +} + +_file_runner__call_function_if_exists() { + local function=$1 + shift 1 + if array__contains "${function}" "${public_functions[@]}"; then + "${function}" + fi +} + +_file_runner__check_if_global_setup_has_exited() { + local has_exited="$(database__get "sbu_current_global_setup_has_failed")" + if [[ "${has_exited}" == "${SBU_YES}" ]]; then + _file_runner__handle_failure_in_global_setup + fi +} + +_file_runner__handle_failure_in_global_setup() { + reporter__global_setup_has_failed + _file_runner__skip_all_tests +} + + +parser__get_public_functions_in_file() { + _parser__find_functions_in_file "$1" \ + | _parser__filter_private_functions \ + | awk '{ print $1 }' +} + +_parser__find_functions_in_file() { + grep -o "${SBU_FUNCTION_DECLARATION_REGEX}" "$1" \ + | _parser__get_function_name_from_declaration +} + +_parser__filter_private_functions() { + grep -v "${SBU_PRIVATE_FUNCTION_NAME_REGEX}" +} + +_parser__get_function_name_from_declaration() { + sed "s/${SBU_FUNCTION_DECLARATION_REGEX}/\2/" +} + + +timer__store_current_time() { + local id=$1 + database__put "sbu_beginning_date_$1" "$(system__get_date_in_seconds)" +} + +timer__get_time_elapsed() { + local id=$1 + local beginning_date="$(database__get "sbu_beginning_date_$1")" + local ending_date="$(system__get_date_in_seconds)" + + [[ -n "${beginning_date}" ]] \ + && system__print "$(( ending_date - beginning_date ))" \ + || system__print "0" +} + + +results__test_files_start_running() { + database__put "sbu_successful_tests_count" "0" + database__put "sbu_failing_tests_count" "0" + database__put "sbu_skipped_tests_count" "0" +} + +results__get_successful_tests_count() { + _results__get_tests_count_of_type "successful" +} + +results__increment_successful_tests() { + _results__increment_tests_of_type "successful" +} + +results__get_failing_tests_count() { + _results__get_tests_count_of_type "failing" +} + +results__increment_failing_tests() { + _results__increment_tests_of_type "failing" +} + +results__get_skipped_tests_count() { + _results__get_tests_count_of_type "skipped" +} + +results__increment_skipped_tests() { + _results__increment_tests_of_type "skipped" +} + +results__get_total_tests_count() { + local successes="$(results__get_successful_tests_count)" + local failures="$(results__get_failing_tests_count)" + local skipped="$(results__get_skipped_tests_count)" + system__print "$(( successes + failures + skipped ))" +} + +_results__get_tests_count_of_type() { + local type=$1 + database__get "sbu_${type}_tests_count" +} + +_results__increment_tests_of_type() { + local type=$1 + local count="$(results__get_${type}_tests_count)" + database__put "sbu_${type}_tests_count" "$(( count + 1 ))" +} + + +test_runner__run_test() { + local test_function=$1 + shift 1 + reporter__test_starts_running "${test_function}" + timer__store_current_time "test_time" + ( + _test_runner__call_setup_if_exists "$@" \ + && _test_runner__call_test_fonction "${test_function}" + local setup_and_test_code=$? + _test_runner__call_teardown_if_exists "$@" + (( $? == ${SBU_SUCCESS_STATUS_CODE} \ + && ${setup_and_test_code} == ${SBU_SUCCESS_STATUS_CODE} )) + ) + _test_runner__parse_test_function_result $? + reporter__test_ends_running "$(timer__get_time_elapsed "test_time")" +} + +_test_runner__call_test_fonction() { + ( "$1" >&${SBU_STANDARD_FD} 2>&${SBU_ERROR_FD} ) +} + +_test_runner__call_setup_if_exists() { + _test_runner__call_function_if_exits "${SBU_SETUP_FUNCTION_NAME}" "$@" +} + +_test_runner__call_teardown_if_exists() { + _test_runner__call_function_if_exits "${SBU_TEARDOWN_FUNCTION_NAME}" "$@" +} + +_test_runner__parse_test_function_result() { + if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then + results__increment_successful_tests + reporter__test_has_succeeded + else + results__increment_failing_tests + reporter__test_has_failed + fi +} + +_test_runner__call_function_if_exits() { + local function=$1 + shift 1 + if array__contains "${function}" "$@"; then + "${function}" + fi +} + +test_runner__skip_test() { + local test_function=$1 + reporter__test_starts_running "${test_function}" + results__increment_skipped_tests + reporter__test_is_skipped "${test_function}" + reporter__test_ends_running 0 +} + + +reporter__test_files_start_running() { + _reporter__initialise_file_descriptors + reporter__for_each_reporter \ + _reporter__call_function "test_files_start_running" "$@" +} + +_reporter__initialise_file_descriptors() { + eval "exec ${SBU_STANDARD_FD}>&1" + eval "exec ${SBU_ERROR_FD}>&2" +} + +reporter__global_setup_has_failed() { + reporter__for_each_reporter \ + _reporter__call_function "global_setup_has_failed" "$@" +} + +reporter__test_file_starts_running() { + reporter__for_each_reporter \ + _reporter__call_function "test_file_starts_running" "$@" +} + +reporter__test_starts_running() { + reporter__for_each_reporter \ + _reporter__call_function "test_starts_running" "$@" +} + +reporter__test_has_succeeded() { + reporter__for_each_reporter \ + _reporter__call_function "test_has_succeeded" "$@" +} + +reporter__test_has_failed() { + reporter__for_each_reporter \ + _reporter__call_function "test_has_failed" "$@" +} + +reporter__test_is_skipped() { + reporter__for_each_reporter \ + _reporter__call_function "test_is_skipped" "$@" +} + +reporter__test_ends_running() { + reporter__for_each_reporter \ + _reporter__call_function "test_ends_running" "$@" +} + +reporter__test_file_ends_running() { + reporter__for_each_reporter \ + _reporter__call_function "test_file_ends_running" "$@" +} + +reporter__test_files_end_running() { + reporter__for_each_reporter \ + _reporter__call_function "test_files_end_running" "$@" + _reporter__release_file_descriptors +} + +_reporter__release_file_descriptors() { + eval "exec 1>&${SBU_STANDARD_FD} ${SBU_STANDARD_FD}>&-" + eval "exec 2>&${SBU_ERROR_FD} ${SBU_ERROR_FD}>&-" +} + +_reporter__call_function() { + local function=$1 + shift 1 + "${reporter}_reporter__${function}" "$@" +} + +reporter__for_each_reporter() { + local reporter + for reporter in ${SBU_REPORTERS//${SBU_VALUE_SEPARATOR}/ }; do + "$@" + done +} + +reporter__print_with_color() { + system__print_with_color "$@" >&${SBU_STANDARD_FD} +} + +reporter__print_line() { + system__print_line "$@" >&${SBU_STANDARD_FD} +} + +reporter__print_line_with_color() { + system__print_line_with_color "$@" >&${SBU_STANDARD_FD} +} + +reporter__print_new_line() { + system__print_new_line >&${SBU_STANDARD_FD} +} + +reporter__get_color_code_for_tests_result() { + local color_code=${SBU_GREEN_COLOR_CODE} + if ! runner__tests_are_successful; then + color_code=${SBU_RED_COLOR_CODE} + fi + system__print "${color_code}" +} + +reporter__get_test_file_relative_name() { + system__print "${1#${SBU_BASE_TEST_DIRECTORY}\/}" +} + + +simple_reporter__test_files_start_running() { + : +} + +simple_reporter__test_file_starts_running() { + local relative_name="$(reporter__get_test_file_relative_name "$1")" + reporter__print_line "[File] ${relative_name}" +} + +simple_reporter__global_setup_has_failed() { + reporter__print_line_with_color \ + "Global setup has failed" ${SBU_YELLOW_COLOR_CODE} +} + +simple_reporter__test_starts_running() { + reporter__print_line "[Test] $1" +} + +simple_reporter__test_has_succeeded() { + reporter__print_line_with_color "OK" ${SBU_GREEN_COLOR_CODE} +} + +simple_reporter__test_has_failed() { + reporter__print_line_with_color "KO" ${SBU_RED_COLOR_CODE} +} + +simple_reporter__test_is_skipped() { + reporter__print_line_with_color "Skipped" ${SBU_YELLOW_COLOR_CODE} +} + +simple_reporter__test_ends_running() { + : +} + +simple_reporter__test_file_ends_running() { + reporter__print_new_line +} + +simple_reporter__test_files_end_running() { + local time="in $1s" + reporter__print_line "[Results]" + local color="$(reporter__get_color_code_for_tests_result)" + local total_count="$(_simple_reporter__get_total_count_message)" + local failures_count="$(_simple_reporter__get_failures_count_message)" + local skipped_count="$(results__get_skipped_tests_count) skipped" + local message="${total_count}, ${failures_count}, ${skipped_count} ${time}" + reporter__print_line_with_color "${message}" "${color}" +} + +_simple_reporter__get_total_count_message() { + local count="$(results__get_total_tests_count)" + system__print "${count} test$(_simple_reporter__get_agreement ${count})" +} + +_simple_reporter__get_failures_count_message() { + local count="$(results__get_failing_tests_count)" + system__print "${count} failure$(_simple_reporter__get_agreement ${count})" +} + +_simple_reporter__get_agreement() { + (( $1 > 1 )) \ + && system__print "s" \ + || system__print "" +} + + +dots_reporter__test_files_start_running() { + exec 1>/dev/null + exec 2>/dev/null +} + +dots_reporter__test_file_starts_running() { + : +} + +dots_reporter__global_setup_has_failed() { + : +} + +dots_reporter__test_starts_running() { + : +} + +dots_reporter__test_has_succeeded() { + reporter__print_with_color "." ${SBU_GREEN_COLOR_CODE} +} + +dots_reporter__test_has_failed() { + reporter__print_with_color "F" ${SBU_RED_COLOR_CODE} +} + +dots_reporter__test_is_skipped() { + reporter__print_with_color "S" ${SBU_YELLOW_COLOR_CODE} +} + +dots_reporter__test_ends_running() { + : +} + +dots_reporter__test_file_ends_running() { + : +} + +dots_reporter__test_files_end_running() { + local color="$(reporter__get_color_code_for_tests_result)" + local texte="$(runner__tests_are_successful \ + && system__print "OK" \ + || system__print "KO")" + reporter__print_line_with_color "${texte}" "${color}" +} + + +junit_reporter__test_files_start_running() { + _junit_reporter__initialise_report_with \ + "" + _junit_reporter__write_line_to_report "" +} + +junit_reporter__test_file_starts_running() { + local file_name=$1 + local test_count=$2 + local suite_name="$(_junit_reporter__get_suite_name "${file_name}")" + database__put "sbu_current_suite_name" "${suite_name}" + _junit_reporter__write_line_to_report \ + " " + _junit_reporter__delete_all_outputs_lines "suite" + _junit_reporter__redirect_outputs_to_database "suite" +} + +junit_reporter__global_setup_has_failed() { + : +} + +junit_reporter__test_starts_running() { + local suite_name="$(database__get "sbu_current_suite_name")" + local test_name="$(xml__encode_text "$1")" + _junit_reporter__write_line_to_report \ + " " + _junit_reporter__delete_all_outputs_lines "test" + _junit_reporter__redirect_outputs_to_database "test" +} + +junit_reporter__test_has_succeeded() { + : +} + +junit_reporter__test_has_failed() { + _junit_reporter__write_line_to_report " " + _junit_reporter__write_line_to_report " " +} + +junit_reporter__test_is_skipped() { + _junit_reporter__write_line_to_report " " + _junit_reporter__write_line_to_report " " +} + +junit_reporter__test_ends_running() { + _junit_reporter__redirect_outputs_to_database "suite" + _junit_reporter__write_time_in_current_test_case_tag_in_report "$1" + _junit_reporter__flush_all_outputs_to_report_if_any "test" + _junit_reporter__write_line_to_report " " +} + +_junit_reporter__write_time_in_current_test_case_tag_in_report() { + local test_time=$1 + local report_content=$(cat "${SBU_JUNIT_REPORTER_OUTPUT_FILE}") + local content_with_time="$(system__substitute_variable \ + "${report_content}" "sbu_current_test_time" "${test_time}")" + system__print_line \ + "${content_with_time}" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}" +} + +junit_reporter__test_file_ends_running() { + _junit_reporter__flush_all_outputs_to_report_if_any "suite" + _junit_reporter__write_line_to_report " " + database__put "sbu_current_suite_name" "" +} + +junit_reporter__test_files_end_running() { + _junit_reporter__write_line_to_report "" +} + +_junit_reporter__get_suite_name() { + local relative_name="$(reporter__get_test_file_relative_name "$1")" + local dots_replaced_by_underscores="${relative_name//./_}" + local slashes_replaced_by_dots="${dots_replaced_by_underscores//\//.}" + xml__encode_text "${slashes_replaced_by_dots}" +} + +_junit_reporter__initialise_report_with() { + system__print_line "$1" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}" +} + +_junit_reporter__write_line_to_report() { + system__print_line "$1" >> "${SBU_JUNIT_REPORTER_OUTPUT_FILE}" +} + +_junit_reporter__redirect_outputs_to_database() { + local scope=$1 + exec 1>>\ + "$(database__get_descriptor "sbu_current_${scope}_standard_ouputs_lines")" + exec 2>>\ + "$(database__get_descriptor "sbu_current_${scope}_error_ouputs_lines")" +} + +_junit_reporter__delete_all_outputs_lines() { + database__put "sbu_current_$1_standard_ouputs_lines" + database__put "sbu_current_$1_error_ouputs_lines" +} + +_junit_reporter__flush_all_outputs_to_report_if_any() { + _junit_reporter__flush_outputs_to_report_if_any "$1" "standard" + _junit_reporter__flush_outputs_to_report_if_any "$1" "error" +} + +_junit_reporter__flush_outputs_to_report_if_any() { + local scope=$1 + local outputs_type=$2 + local key="sbu_current_${scope}_${outputs_type}_ouputs_lines" + local outputs="$(database__get "${key}")" + if [[ -n "${outputs}" ]]; then + _junit_reporter__write_outputs_to_report \ + "${scope}" "${outputs_type}" "${outputs}" + database__put "${key}" "" + fi +} + +_junit_reporter__write_outputs_to_report() { + local scope=$1 + local outputs_type=$2 + local outputs=$3 + local tag="$(_junit_reporter__get_tag_for_outputs_type "${outputs_type}")" + local indentation="$(_junit_reporter__get_indentation_for_scope "${scope}")" + _junit_reporter__write_line_to_report "${indentation}<${tag}>" + _junit_reporter__write_line_to_report "$(xml__encode_text "${outputs}")" + _junit_reporter__write_line_to_report "${indentation}" +} + +_junit_reporter__get_tag_for_outputs_type() { + [[ "$1" == "standard" ]] \ + && system__print "system-out" \ + || system__print "system-err" +} + +_junit_reporter__get_indentation_for_scope() { + [[ "$1" == "suite" ]] \ + && system__print " " \ + || system__print " " +} + + +xml__encode_text() { + local encoded=${1//\&/\&\;} + encoded=${encoded//\/\>\;} + encoded=${encoded//\"/\"\;} + encoded=${encoded//\'/\&apos\;} + system__print "${encoded}" +} + + +database__initialise() { + _SBU_DB_TOKEN="$(system__random)" + _database__ensure_directory_exists +} + +database__release() { + rm -rf "$(_database__get_dir)" +} + +database__put() { + _database__ensure_directory_exists + system__print "$2" > "$(_database__get_dir)/$1" +} + +database__post() { + _database__ensure_directory_exists + system__print "$2" >> "$(_database__get_dir)/$1" +} + +database__post_line() { + _database__ensure_directory_exists + system__print_line "$2" >> "$(_database__get_dir)/$1" +} + +database__put_variable() { + _database__ensure_directory_exists + database__put "$1" "${!1}" +} + +database__get() { + [[ -e "$(_database__get_dir)/$1" ]] && cat "$(_database__get_dir)/$1" +} + +database__get_descriptor() { + system__print "$(_database__get_dir)/$1" +} + +_database__ensure_directory_exists() { + mkdir -p "$(_database__get_dir)" +} + +_database__get_dir() { + system__print "${SBU_TEMP_DIR}/database/${_SBU_DB_TOKEN}" +} + + +system__get_string_or_default() { + [[ -n "$1" ]] \ + && system__print "$1" \ + || system__print "$2" +} + +system__get_date_in_seconds() { + date +%s +} + +system__print_line_with_color() { + system__print_with_color "$@" + system__print_new_line +} + +system__print_with_color() { + if [[ "${SBU_USE_COLORS}" == "${SBU_YES}" ]]; then + printf "$2$1${SBU_DEFAULT_COLOR_CODE}" + else + system__print "$1" + fi +} + +system__print_line() { + system__print "$1" + system__print_new_line +} + +system__print() { + printf "%s" "$1" +} + +system__print_new_line() { + printf "\n" +} + +array__contains() { + local value=$1 + shift 1 + local i + for (( i=1; i <= $#; i++ )); do + if [[ "${!i}" == "${value}" ]]; then + return ${SBU_SUCCESS_STATUS_CODE} + fi + done + return ${SBU_FAILURE_STATUS_CODE} +} + +array__from_lines() { + local IFS=$'\n' + eval "$1=(\$( 0 )); do + local random_index=$(( $(system__random) % ${#copy[@]} )) + system__print_line "${copy[${random_index}]}" + unset copy[${random_index}] + copy=("${copy[@]}") + done +} + +system__random() { + system__print "${RANDOM}" +} + +system__substitute_variable() { + local string=$1 + local key="\$\{$2\}" + local value=$3 + printf "%s" "${string//${key}/${value}}" +} + + +main__main() { + configuration__load + _main__initialise + local parsed_arguments=0 + _main__parse_arguments "$@" + shift ${parsed_arguments} + _main__assert_only_one_argument_left $# + _main__assert_reporters_are_known + SBU_BASE_TEST_DIRECTORY=$1 + + if [[ "${SBU_NO_RUN}" != "${SBU_YES}" ]]; then + runner__run_all_test_files "$1" + return $? + fi +} + +_main__initialise() { + database__initialise + trap _main__release EXIT +} + +_main__release() { + database__release +} + +_main__parse_arguments() { + local argument + for argument in "$@"; do + case "${argument}" in + -a|--api-cheat-sheet) + _main__print_api_cheat_sheet_and_exit + ;; + -c=*|--colors=*) + SBU_USE_COLORS="${argument#*=}" + (( parsed_arguments++ )) + ;; + -d=*|--random-run=*) + SBU_RANDOM_RUN="${argument#*=}" + (( parsed_arguments++ )) + ;; + -h|--help) + _main__print_full_usage + exit ${SBU_SUCCESS_STATUS_CODE} + ;; + -f=*|--file-pattern=*) + SBU_TEST_FILE_PATTERN="${argument#*=}" + (( parsed_arguments++ )) + ;; + --no-run) + SBU_NO_RUN="${SBU_YES}" + (( parsed_arguments++ )) + ;; + -o=*|--output-file=*) + SBU_JUNIT_REPORTER_OUTPUT_FILE="${argument#*=}" + (( parsed_arguments++ )) + ;; + -t=*|--test-pattern=*) + SBU_TEST_FUNCTION_PATTERN="${argument#*=}" + (( parsed_arguments++ )) + ;; + -r=*|--reporters=*) + SBU_REPORTERS="${argument#*=}" + (( parsed_arguments++ )) + ;; + -*|--*) + _main__print_illegal_option "${argument}" + _main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE} + ;; + esac + done +} + + _main__assert_reporters_are_known() { + reporter__for_each_reporter _main__fail_if_reporter_unknown +} + +_main__fail_if_reporter_unknown() { + if ! array__contains "${reporter}" "simple" "dots" "junit"; then + system__print_line \ + "$(_main__get_script_name): unknown reporter <${reporter}>" + exit ${SBU_FAILURE_STATUS_CODE} + fi +} + +_main__print_illegal_option() { + local option="${1%=*}" + option="${option#-}" + option="${option#-}" + system__print_line "$(_main__get_script_name): illegal option -- ${option}" +} + +_main__assert_only_one_argument_left() { + if (( $1 > 1 )); then + system__print_line "$(_main__get_script_name): only one path is allowed" + _main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE} + fi +} + +_main__get_script_name() { + basename "${BASH_SOURCE[0]}" +} + +_main__print_usage_and_exit_with_code() { + _main__print_usage + exit $1 +} + +_main__print_full_usage() { + _main__print_usage + local script="$(_main__get_script_name)" + system__print_new_line + system__print_line "\ +[options] + -a, --api-cheat-sheet + print api cheat sheet like assertions + -c, --colors=${SBU_YES} or ${SBU_NO} + tests output with colors or no + -d, --random-run=${SBU_YES} or ${SBU_NO} + tests files and functions randomly run or no + -f, --file-pattern= + pattern to filter test files + -h + print usage + -o, --output-file= + output file for JUnit reporter + -r, --reporters= + comma-separated reporters (simple, dots or junit) + -t, --test-pattern= + pattern to filter test function in files + +[examples] + ${script} . + run all tests in current directory + ${script} -p=*test.sh sources/test + run all tests files ending with test.sh in sources/test" +} + +_main__print_usage() { + system__print_line "\ +usage: $(_main__get_script_name) [options] path + run all tests in path" +} + +_main__print_api_cheat_sheet_and_exit() { + system__print_line "\ +[assertions] + assertion__equal (value, other) + -> assert that is equal to + assertion__different (value, other) + -> assert that is different from + assertion__string_contains (string, substring) + -> assert that contains + assertion__string_does_not_contain (string, substring) + -> assert that does not contain + assertion__string_empty (string) + -> assert that is empty + assertion__string_not_empty (string) + -> assert that is not empty + assertion__array_contains (element, array[0], array[1], ...) + -> assert that the contains the + assertion__array_does_not_contain (element, array elements...) + -> assert that the does not contain the + assertion__successful (command) + -> assert that the is successful + assertion__failing (command) + -> assert that the is failing + assertion__status_code_is_success (code) + -> assert that the status is 0 + assertion__status_code_is_failure (code) + -> assert that the status is not 0 + +[special functions] + ${SBU_GLOBAL_SETUP_FUNCTION_NAME} + -> Executed before all tests in a file + ${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME} + -> Executed after all tests in a file + ${SBU_SETUP_FUNCTION_NAME} + -> Executed before each test in a file + ${SBU_TEARDOWN_FUNCTION_NAME} + -> Executed after each test in a file + +[mocks] + mock__make_function_do_nothing (function_to_mock) + -> make function do nothing + mock__make_function_prints (function_to_mock, message) + -> make function prints a message + mock__make_function_call (function_to_mock, function_to_call) + -> make function call another function" + exit ${SBU_SUCCESS_STATUS_CODE} +} + + +main__main "$@" diff --git a/tests/templates/inventory-gce.j2 b/tests/templates/inventory-gce.j2 new file mode 100644 index 000000000..72ad469de --- /dev/null +++ b/tests/templates/inventory-gce.j2 @@ -0,0 +1,20 @@ +node1 ansible_ssh_host={{gce.instance_data[0].public_ip}} +node2 ansible_ssh_host={{gce.instance_data[1].public_ip}} +node3 ansible_ssh_host={{gce.instance_data[2].public_ip}} + +[kube-master] +node1 +node2 + +[kube-node] +node1 +node2 +node3 + +[etcd] +node1 +node2 + +[k8s-cluster:children] +kube-node +kube-master diff --git a/tests/templates/inventory.ini.j2 b/tests/templates/inventory.ini.j2 new file mode 100644 index 000000000..d6358efb9 --- /dev/null +++ b/tests/templates/inventory.ini.j2 @@ -0,0 +1,20 @@ +node1 ansible_ssh_host={{ec2.instances[0].public_ip}} ansible_ssh_user=admin +node2 ansible_ssh_host={{ec2.instances[1].public_ip}} ansible_ssh_user=admin +node3 ansible_ssh_host={{ec2.instances[2].public_ip}} ansible_ssh_user=admin + +[kube-master] +node1 +node2 + +[kube-node] +node1 +node2 +node3 + +[etcd] +node1 +node2 + +[k8s-cluster:children] +kube-node +kube-master diff --git a/tests/testcases/010_check-apiserver.yml b/tests/testcases/010_check-apiserver.yml new file mode 100644 index 000000000..7beed06fb --- /dev/null +++ b/tests/testcases/010_check-apiserver.yml @@ -0,0 +1,12 @@ +--- +- hosts: kube-master + + tasks: + - name: Check the API servers are responding + local_action: + module: uri + url: https://{{ansible_ssh_host}}/api/v1 + user: kube + password: changeme + validate_certs: no + status_code: 200 diff --git a/tests/testcases/020_check-create-pod.yml b/tests/testcases/020_check-create-pod.yml new file mode 100644 index 000000000..219fbb985 --- /dev/null +++ b/tests/testcases/020_check-create-pod.yml @@ -0,0 +1,13 @@ +--- +- hosts: node1 + + tasks: + - name: Run a replica controller composed of 2 pods + shell: "kubectl run test --image=busybox --replicas=2 --command -- tail -f /dev/null" + + - name: Pods are running + shell: "kubectl get pods --no-headers -o json" + register: run_pods_log + until: (run_pods_log.stdout | from_json)['items'] | map(attribute = 'status.phase') | join(',') == "Running,Running" + retries: 24 + delay: 5 diff --git a/tests/testcases/030_check-network.yml b/tests/testcases/030_check-network.yml new file mode 100644 index 000000000..596ce0ce1 --- /dev/null +++ b/tests/testcases/030_check-network.yml @@ -0,0 +1,21 @@ +--- +- hosts: node1 + + tasks: + + - name: Get pod names + shell: "kubectl get pods -o json" + register: pods + + - set_fact: + pod_names: "{{ (pods.stdout | from_json)['items'] | map(attribute = 'metadata.name') | list }}" + pod_ips: "{{ (pods.stdout | from_json)['items'] | map(attribute = 'status.podIP') | list }}" + + - name: Check pods IP are in correct network + assert: + that: item | ipaddr(kube_pods_subnet) + with_items: pod_ips + + + - name: Ping between pods is working + shell: "kubectl exec {{pod_names[0]}} -- ping -c 4 {{ pod_ips[1] }}"