Fix functional tests & multiple bugfixes & use CES

This commit is contained in:
Brendan LE GLAUNEC
2016-11-12 12:55:32 +01:00
committed by Brendan Le Glaunec
parent 5be5124e70
commit 58bcfb9ee5
34 changed files with 663 additions and 356 deletions
+28
View File
@@ -2,6 +2,34 @@
This file lists all versions of the repository and precises all changes. This file lists all versions of the repository and precises all changes.
## v1.1.1
#### Minor changes :
* Removed unnecessary null pointer checks (thanks to https://github.com/elfring)
* Updated package description
* Removed debug message in CMake build
* Added `/ch01.264` to the URL dictionary in the deployment (Comelit default RTSP URL)
* Updated tests partially (still needs work to make the code cleaner)
* Variable names are now compliant with Golang best practices
* JSON variable names are back to normal
* Functions have been moved in more appropriate source files
* Structure definitions have been moved in more appropriate source files
* Source files have been renamed to be more relevant
* JUnit output now considers each camera as a test case
* JUnit output now contains errors which makes debugging much easier
* Added header files where it was forgotten
#### Bugfixes :
* Fixed an issue where if you loose your internet connection during thumbnail generation, FFMpeg would get stuck forever and thus Cameradar would never finish
* Fixed an issue where multithreading could cause crashes
* Fixed an issue where the routes dictionary was mistaken for the credentials dictionary
* Fixed issues with the golang testing tool
* Fixed automated camera generation
* Fixed docker IP address resolution
#### Known issues :
* There is an issue with Camera Emulation Server that makes it impossible for Cameradar to generate thumbnails, which is why right now the verification of the thumbnails presence is commented and it is assumed correct. It is probably an issue with GST-RTSP-Server but requires investigation.
## v1.1.0 ## v1.1.0
#### Major changes : #### Major changes :
+5 -7
View File
@@ -19,11 +19,9 @@ set (PROJECT_NAME cameradar)
project (${PROJECT_NAME}) project (${PROJECT_NAME})
message ("Here")
set (${PROJECT_NAME}_VERSION_MAJOR 1) set (${PROJECT_NAME}_VERSION_MAJOR 1)
set (${PROJECT_NAME}_VERSION_MINOR 1) set (${PROJECT_NAME}_VERSION_MINOR 1)
set (${PROJECT_NAME}_VERSION_PATCH 0) set (${PROJECT_NAME}_VERSION_PATCH 1)
set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}") set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}")
find_package(Git REQUIRED) find_package(Git REQUIRED)
@@ -34,7 +32,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wextra -Wno-unused-function")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") #enable error coloration on gcc set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") #enable error coloration on gcc
# release specific flags # release specific flags
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") #enable error coloration on gcc set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
#debug specific flags #debug specific flags
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
@@ -102,7 +100,7 @@ include_directories (
set (${CAMERADAR_BINARIES} "") set (${CAMERADAR_BINARIES} "")
set (${CAMERADAR_LIBRARIES} "") set (${CAMERADAR_LIBRARIES} "")
#build cache managers # Build cache managers
add_subdirectory (deps) add_subdirectory (deps)
message ("Debug") message ("Debug")
add_subdirectory (cameradar_standalone) add_subdirectory (cameradar_standalone)
@@ -115,11 +113,11 @@ install (FILES ${CAMERADAR_CACHE_MANAGERS} DESTINATION cache_managers)
install (FILES ${CAMERADAR_LIBRARIES} DESTINATION libraries) install (FILES ${CAMERADAR_LIBRARIES} DESTINATION libraries)
install (DIRECTORY ${CMAKE_SOURCE_DIR}/deps/licenses DESTINATION libraries) install (DIRECTORY ${CMAKE_SOURCE_DIR}/deps/licenses DESTINATION libraries)
# cpack configuration # CPack configuration
include (InstallRequiredSystemLibraries) include (InstallRequiredSystemLibraries)
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar")
set (CPACK_PACKAGE_VENDOR "Etix Labs") set (CPACK_PACKAGE_VENDOR "Etix Labs")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar tool") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cameradar hacks its way into RTSP CCTV cameras")
set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${${PROJECT_NAME}_VERSION}_${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}") set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${${PROJECT_NAME}_VERSION}_${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}")
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+1 -1
View File
@@ -3,7 +3,7 @@
## An RTSP surveillance camera access multitool ## An RTSP surveillance camera access multitool
[![cameradar License](https://img.shields.io/badge/license-Apache-blue.svg)](#license) [![cameradar License](https://img.shields.io/badge/license-Apache-blue.svg)](#license)
[![Latest release](https://img.shields.io/badge/release-1.1.0-green.svg)](https://github.com/EtixLabs/cameradar/releases/latest) [![Latest release](https://img.shields.io/badge/release-1.1.1-green.svg)](https://github.com/EtixLabs/cameradar/releases/latest)
#### Cameradar allows you to: #### Cameradar allows you to:
+7 -16
View File
@@ -1,25 +1,16 @@
{ {
"mysql_db" : { "mysql_db" : {
"host" : "0.0.0.0", "host" : "cameradar-database",
"port" : 3306, "port" : 3306,
"user": "root", "user": "root",
"password": "root", "password": "root",
"db_name": "cctv_dev" "db_name": "cmrdr"
}, },
"subnets" : "localhost",
"subnets" : "172.16.100.11",
// If not specified, will scan all ports (1-65535)
"ports" : "554,8554", "ports" : "554,8554",
"rtsp_url_file" : "conf/url.json", "rtsp_url_file" : "/cameradar/conf/url.json",
"rtsp_ids_file" : "conf/ids.json", "rtsp_ids_file" : "/cameradar/conf/ids.json",
"thumbnail_storage_path" : "/tmp/thumbs",
// You must give an accessible path to an already existing directory "cache_manager_path" : "/cameradar/cache_managers",
"thumbnail_storage_path" : "/tmp",
// This is the path that will be used in the Docker container
// if you're not familiar with Docker, only change the
// cache_manager_name value
"cache_manager_path" : "../cache_managers",
"cache_manager_name" : "dumb" "cache_manager_name" : "dumb"
} }
+2 -2
View File
@@ -92,12 +92,12 @@ bool
configuration::load_url() { configuration::load_url() {
std::string content; std::string content;
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration"); LOG_DEBUG_("Trying to open url file from " + this->rtsp_url_file, "configuration");
if (this->rtsp_url_file.size()) { if (this->rtsp_url_file.size()) {
content = read_file(this->rtsp_url_file.c_str()).second; content = read_file(this->rtsp_url_file.c_str()).second;
} else { } else {
LOG_WARN_( LOG_WARN_(
"No ids file detected in your configuration, Cameradar will use " "No url file detected in your configuration, Cameradar will use "
"the default one " "the default one "
"instead.", "instead.",
"configuration"); "configuration");
+3 -3
View File
@@ -25,8 +25,8 @@ size_t
write_data(void* buffer, size_t size, size_t nmemb, void* userp) { write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
// I'm sorry for this // I'm sorry for this
// Forget you ever saw it // Forget you ever saw it
(void)buffer; if (not buffer || not size || not nmemb) return 0;
(void)userp;
return size * nmemb; return size * nmemb;
} }
@@ -88,7 +88,7 @@ curl_describe(const std::string& path, bool logs) {
m.lock(); m.lock();
curl_global_cleanup(); curl_global_cleanup();
m.unlock(); m.unlock();
LOG_DEBUG_("Response code : " + std::to_string(rc), "describe"); LOG_DEBUG_("[" + path + "] Response code : " + std::to_string(rc), "describe");
if (logs) { if (logs) {
// Some cameras return 400 instead of 401, don't know why. // Some cameras return 400 instead of 401, don't know why.
// Some cameras timeout and then curl considers the status as 0 // Some cameras timeout and then curl considers the status as 0
+8 -1
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <rtsp_path.h>
#include <logger.h> #include <logger.h>
#include <rtsp_path.h>
namespace etix { namespace etix {
@@ -21,10 +21,17 @@ namespace cameradar {
const std::string const std::string
make_path(const stream_model& model) { make_path(const stream_model& model) {
if (model.password != "" || model.username != "") {
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" + std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
model.address + ":" + std::to_string(model.port) + model.route); model.address + ":" + std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug"); LOG_DEBUG_(ret, "debug");
return ret; return ret;
} else {
std::string ret(model.service_name + "://" + model.address + ":" +
std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug");
return ret;
}
} }
} }
} }
+13 -5
View File
@@ -1,8 +1,16 @@
# Copyright (C) 2015 Etix Labs - All Rights Reserved. ## Copyright 2016 Etix Labs
# All information contained herein is, and remains the property of Etix Labs and its suppliers, ##
# if any. The intellectual and technical concepts contained herein are proprietary to Etix Labs ## Licensed under the Apache License, Version 2.0 (the "License");
# Dissemination of this information or reproduction of this material is strictly forbidden unless ## you may not use this file except in compliance with the License.
# prior written permission is obtained from Etix Labs. ## 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.
# MySQL Connector dependency # MySQL Connector dependency
message(STATUS "Configuring deps.mysqlconnector") message(STATUS "Configuring deps.mysqlconnector")
+7 -1
View File
@@ -9,10 +9,16 @@ COL_BLUE=$ESC_SEQ"34;01m"
COL_MAGENTA=$ESC_SEQ"35;01m" COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m" COL_CYAN=$ESC_SEQ"36;01m"
echo -e $COL_YELLOW"Deleting old package ... "$COL_RESET
rm -f cameradar_*_Release_Linux.tar.gz
echo -e $COL_GREEN"OK!"$COL_RESET
echo -e $COL_YELLOW"Creating package ... "$COL_RESET echo -e $COL_YELLOW"Creating package ... "$COL_RESET
{ cd .. {
cd ..
mkdir build mkdir build
cd build cd build
rm -f cameradar_*_Release_Linux.tar.gz
cmake .. -DCMAKE_BUILD_TYPE=Release cmake .. -DCMAKE_BUILD_TYPE=Release
make package make package
cp cameradar_*_Release_Linux.tar.gz ../deployment cp cameradar_*_Release_Linux.tar.gz ../deployment
Binary file not shown.
Binary file not shown.
+1
View File
@@ -24,6 +24,7 @@
"/camera.stm", "/camera.stm",
"/ch0", "/ch0",
"/ch001.sdp", "/ch001.sdp",
"/ch01.264",
"/ch0_unicast_firststream", "/ch0_unicast_firststream",
"/ch0_unicast_secondstream", "/ch0_unicast_secondstream",
"/channel1", "/channel1",
+9 -7
View File
@@ -2,14 +2,14 @@ FROM ubuntu:15.10
MAINTAINER brendan.leglaunec@etixgroup.com MAINTAINER brendan.leglaunec@etixgroup.com
ENV LD_LIBRARY_PATH="/cctv/libraries" ENV LD_LIBRARY_PATH="/cameradar/libraries"
# install go # install go
RUN apt-get update && apt-get install -y make git wget curl RUN apt-get update && apt-get install -y make git wget curl
RUN wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz RUN wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz RUN tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
# set variable env # set variable env
ENV GOPATH=/go ENV GOPATH=/cameradartest/go
ENV PATH=$PATH:/go/bin ENV PATH=$PATH:/go/bin
ENV PATH=$PATH:/usr/local/go/bin ENV PATH=$PATH:/usr/local/go/bin
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
@@ -27,17 +27,19 @@ RUN apt-get update && apt-get install -y \
RUN apt-get install -y psmisc RUN apt-get install -y psmisc
ADD cctv_*_Debug_Linux.tar.gz / ADD cameradar_*_Debug_Linux.tar.gz /
RUN mv cctv_*_Debug_Linux cctv RUN mv cameradar_*_Debug_Linux cameradar
# create cameradaratest folder in go src path # create cameradaratest folder in go src path
RUN mkdir -p /go/src/cameradartest RUN mkdir -p /cameradartest/go/src/cameradartest
ADD ./conf /conf COPY src/*.go /cameradartest/go/src/cameradartest/
COPY ./conf /conf
ADD ./docker/run_cameradartest.sh /run.sh ADD ./docker/run_cameradartest.sh /run.sh
# get go deps # get go deps
RUN go get github.com/go-sql-driver/mysql RUN go get github.com/go-sql-driver/mysql
RUN mkdir /thumbnails RUN mkdir /thumbnails
WORKDIR /go/src/cameradartest WORKDIR /cameradartest/go/src/cameradartest
RUN go build -o cameradartest *.go
CMD ["/run.sh"] CMD ["/run.sh"]
+16 -7
View File
@@ -1,14 +1,23 @@
FROM ubuntu:16.04 FROM ubuntu:16.04
MAINTAINER brendan.leglaunec@etixgroup.com MAINTAINER brendan.leglaunec@etixgroup.com
RUN useradd -m vlc; \ RUN apt-get update && apt-get install -y \
apt-get update; \ libgstrtspserver-1.0-dev \
apt-get install -y vlc-nox libgstreamer1.0-dev \
gstreamer1.0-plugins-base \
RUN sed -i s/geteuid/getppid/g /usr/bin/vlc gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
gstreamer1.0-tools \
libssl-dev mysql-client \
gstreamer1.0-plugins-good \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev
ADD ./docker/screen.png /vlc/screen.png ADD ./docker/screen.png /vlc/screen.png
COPY ./docker/run_vlc.sh /start.sh COPY ./docker/run_ces.sh /start.sh
COPY ./etix_rtsp_server /etix_rtsp_server COPY ./camera_emulation_server /camera_emulation_server
EXPOSE 8554 EXPOSE 8554
RUN ./camera_emulation_server&
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
ESC_SEQ="\x1b["
COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
COL_BLUE=$ESC_SEQ"34;01m"
COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m"
echo -e $COL_YELLOW"Deleting old package ... "$COL_RESET
rm -f cameradar_*_Debug_Linux.tar.gz
echo -e $COL_GREEN"OK!"$COL_RESET
echo -e $COL_YELLOW"Creating package ... "$COL_RESET
{
cd ..
mkdir build
cd build
rm -f cameradar_*_Debug_Linux.tar.gz
cmake .. -DCMAKE_BUILD_TYPE=Debug
make package
cp cameradar_*_Debug_Linux.tar.gz ../test
cd ../test
} &> /dev/null
echo -e $COL_GREEN"OK!"$COL_RESET
BIN
View File
Binary file not shown.
+9 -7
View File
@@ -1,14 +1,16 @@
{ {
"mysql_db" : { "mysql_db" : {
"host" : "0.0.0.0", "host" : "cameradar-database",
"port" : 3306, "port" : 3306,
"user": "root", "user": "root",
"password": "root", "password": "root",
"db_name": "cctv" "db_name": "cmrdr"
}, },
"subnets" : "172.16.100.13 localhost", "subnets" : "localhost",
"ports" : "554,8554", // if not specified, default will be 1-65535 "ports" : "554,8554",
"rtsp_url_file" : "conf/url.json", "rtsp_url_file" : "/conf/url.json",
"rtsp_ids_file" : "conf/ids.json", "rtsp_ids_file" : "/conf/ids.json",
"thumbnail_storage_path" : "/ce/que/tu/veux" "thumbnail_storage_path" : "/tmp",
"cache_manager_path" : "/cameradar/cache_managers",
"cache_manager_name" : "dumb"
} }
+7 -7
View File
@@ -2,17 +2,17 @@
"Output": "cameratest.log.xml", "Output": "cameratest.log.xml",
"Cameradar" : { "Cameradar" : {
"Path": "/home/ullaakut/Work/cctv_server2/cameradar/test/cameradar", "Path": "/cameradar/cameradar_standalone/cameradar",
"Args": "-l 1 -c tmp_config", "Args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
"Ports": "554,5554,8554", "Ports": "554,5554,8554",
"IdsPath": "conf/ids.json", "IdsPath": "/conf/ids.json",
"RoutesPath": "conf/url.json", "RoutesPath": "/conf/url.json",
"ThumbPath": "/home/ullaakut/.cctv", "ThumbPath": "/tmp",
"dbHost": "0.0.0.0", "dbHost": "cameradar-database",
"dbPort": 3306, "dbPort": 3306,
"dbUser": "root", "dbUser": "root",
"dbPassword": "root", "dbPassword": "root",
"dbName": "cctv", "dbName": "cmrdr",
"Console": false "Console": false
}, },
"Tests" : [ "Tests" : [
+16 -15
View File
@@ -1,18 +1,19 @@
{ {
"Output": "cameratest.log.xml", "output": "test-results.xml",
"Cameradar" : {
"Path": "/cctv/bin/cameradar", "cameradar" : {
"Args": "-l 1 -c tmp_config", "path": "/cameradar/bin/cameradar",
"Ports": "554,5554,8554,5548", "args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
"IdsPath": "/conf/ids.json", "ports": "554,5554,8554",
"RoutesPath": "/conf/url.json", "ids_path": "conf/ids.json",
"ThumbPath": "/thumbnails", "routes_path": "conf/url.json",
"dbHost": "mysql_cameradar", "thumb_path": "/tmp",
"dbPort": 3306, "db_host": "cameradar-database",
"dbUser": "root", "db_port": 3306,
"dbPassword": "root", "db_user": "root",
"dbName": "cctv", "db_password": "root",
"Console": false "db_name": "cmrdr",
"console": false
}, },
"Tests" : __CAMERAS__ "tests" : __CAMERAS__
} }
+10 -6
View File
@@ -3,7 +3,7 @@
ports=('8554' '8554' '8554' '8554' '8554' '8554') ports=('8554' '8554' '8554' '8554' '8554' '8554')
users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '') users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '')
passwords=('admin' 'root' '12345' 'ubnt' 'password' '') passwords=('admin' 'root' '12345' 'ubnt' 'password' '')
routes=('live.sdp' 'live.sdp' 'ch001.sdp' '' 'invalid' 'live_mpeg4.sdp') routes=('cam0_0' 'live.sdp' 'ch001.sdp' 'cam' 'invalid' 'live_mpeg4.sdp')
cams_name_pattern="fake_camera_" cams_name_pattern="fake_camera_"
# json generation variable only # json generation variable only
@@ -11,11 +11,16 @@ json="[\n"
first=true first=true
# $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid # $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid
function make_json { function make_json {
# Get all data about the container, this will return three lines
# One empty that we ignore
# the two other ones with the IP of our container
# We take the second one using sed and cut to get only the IPAddress
address="$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CID)"
if [ "$first" = true ] ; then first=false if [ "$first" = true ] ; then first=false
else json="$json,\n"; fi else json="$json,\n"; fi
json="$json{" json="$json{"
json="$json\"address\":\"$1\"," json="$json\"address\":\"$address\","
json="$json\"port\":\"$2\"," json="$json\"port\":$2,"
json="$json\"route\":\"$3\"," json="$json\"route\":\"$3\","
json="$json\"username\":\"$4\"," json="$json\"username\":\"$4\","
json="$json\"password\":\"$5\"," json="$json\"password\":\"$5\","
@@ -50,13 +55,12 @@ function start {
# if conf_idx = 4 -> invalid conf # if conf_idx = 4 -> invalid conf
if [ "$conf_idx" == "4" ] ; then is_valid=false; fi if [ "$conf_idx" == "4" ] ; then is_valid=false; fi
docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route" CID=$(docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route");
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid make_json "$name" "$port" "$route" "$user" "$passw" $is_valid $CID
done done
# finalize json # finalize json
json="$json]" json="$json]"
echo "$json"
} }
function stop { function stop {
+8 -4
View File
@@ -1,15 +1,19 @@
#!/bin/bash #!/bin/bash
while ! mysqladmin ping -h"mysql_cameradar" -P3306 --silent; do while ! mysqladmin ping -h"cameradar-database" -P3306 --silent; do
sleep 1 sleep 1
done done
ls -alhR /conf cat /tmp/tests/cameradartest.conf.json
cat /etc/hosts
# build # build
go build go build
cp /tmp/tests/*.xml ./
# run test # run test
./cameradartest /tmp/tests/cameradartest.conf.json ./cameradartest /tmp/tests/cameradartest.conf.json
cp cameratest.log.xml /tmp/tests/ cat *.xml
cp *.xml /tmp/tests/
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
port=$1
user=$2
passw=$3
route=$4
url=""
# need first argument at least
if [ "$2" == "" ]; then
url="rtsp://:$port/$route"
else
url="rtsp://$user:$passw@:$port/$route"
fi
./camera_emulation_server -u $2 -p $3 -r $4
echo "Stream started on ${url}"
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
port=$1
user=$2
passw=$3
route=$4
url=""
# need first argument at least
if [ "$2" == "" ]; then
url="rtsp://:$port/$route"
else
url="rtsp://$user:$passw@:$port/$route"
fi
./etix_rtsp_server -u $s -p $3 -r $4
# cvlc /vlc/screen.png -I dummy --sout-keep --no-drop-late-frames --no-skip-frames --image-duration 9999 --sout="#transcode{vcodec=h264,fps=15,venc=x264{preset=ultrafast,tune=zerolatency,keyint=30,bframes=0,ref=1,level=30,profile=baseline,hrd=cbr,crf=20,ratetol=1.0,vbv-maxrate=1200,vbv-bufsize=1200,lookahead=0}}:rtp{sdp=$url}" --sout-all
Binary file not shown.
+9 -9
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# check if a debug package exist in the current folder # check if a debug package exist in the current folder
if ! ls ./cctv_*_Debug_Linux.tar.gz 1> /dev/null 2>&1; then if ! ls ./cameradar_*_Debug_Linux.tar.gz 1> /dev/null 2>&1; then
(echo "no debug package in the current folder"; exit 137) (echo "no debug package in the current folder"; exit 137)
exit 137 exit 137
fi fi
@@ -19,40 +19,40 @@ function make_docker_command {
done done
# add mysql libk # add mysql libk
cmd="$cmd --link=\"mysql_cameradar\"" cmd="$cmd --link=\"cameradar-database\""
# add cameradar srcs # add cameradar srcs
cmd="$cmd -v \"`pwd`/src:/go/src/cameradartest\"" cmd="$cmd -v \"`pwd`/src:/go/src/cameradartest\""
# add cmaeradar conf # add cmaeradar conf
cmd="$cmd -v \"`pwd`/:/tmp/tests\"" cmd="$cmd -v \"`pwd`/:/tmp/tests\""
# add container name # add container name
cmd="$cmd -v \"`pwd`/:/tmp/shared\""
# add container name
cmd="$cmd cameradartest" cmd="$cmd cameradartest"
} }
function start_test { function start_test {
make_docker_command $1
./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json ./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json
eval $cmd eval $cmd
make_docker_command $1
./docker/gen_cameras.sh stop ./docker/gen_cameras.sh stop
} }
# build images # build images
echo "building docker images" echo "building docker images"
# building fake-camera container # building fake-camera container
docker build -f Dockerfile-camera -t fake-camera . docker build --no-cache -f Dockerfile-camera -t fake-camera .
# building cameradartest image # building cameradartest image
docker build -t cameradartest . docker build --no-cache -t cameradartest .
# getting mysql # getting mysql
echo "starting mysql" echo "starting mysql"
docker pull mysql:5.7 docker pull mysql:5.7
docker run --name mysql_cameradar -e MYSQL_DATABASE=cctv -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7 docker run --name cameradar-database -e MYSQL_DATABASE=cmrdr -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
start_test 1 start_test 1
start_test 5 start_test 5
# start_test 10
# start_test 20
# stop mysql # stop mysql
echo "stopping mysql" echo "stopping mysql"
docker rm -f mysql_cameradar docker rm -f cameradar-database
+21 -7
View File
@@ -1,3 +1,17 @@
// Copyright 2016 Etix Labs
//
// 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.
package main package main
import ( import (
@@ -6,27 +20,27 @@ import (
"os" "os"
) )
func (m *manager) parseConfig() bool { func (t *Tester) parseConfig() bool {
// Get config file path // Get config file path
confPath := "conf/cameratest.conf.json" confPath := "conf/cameratest.conf.json"
av := len(os.Args) av := len(os.Args)
if av == 2 { if av > 1 {
confPath = os.Args[1] confPath = os.Args[1]
} }
// Load config // Load config
fmt.Printf("Loading config file: %s ... ", confPath) fmt.Printf("Loading Tester configuration file: %s ... ", confPath)
configFile, err := os.Open(confPath) configFile, err := os.Open(confPath)
if err != nil { if err != nil {
fmt.Printf("\nCan't open config file: %s\n", err) fmt.Printf("\nCan't open Tester configuration file: %s\n", err)
return false return false
} }
dec := json.NewDecoder(configFile) dec := json.NewDecoder(configFile)
if err = dec.Decode(&m); err != nil { if err = dec.Decode(&t); err != nil {
fmt.Printf("\nUnable to deserialize config file: %s\n", err) fmt.Printf("\nUnable to deserialize Tester configuration file: %s\n", err)
return false return false
} }
fmt.Println("Configuration file successfully loaded") fmt.Println("Tester configuration file successfully loaded")
return true return true
} }
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"database/sql"
"fmt"
"strconv"
_ "github.com/go-sql-driver/mysql"
)
// MysqlDB contains the MySQL configuration
type MysqlDB struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
DbName string `json:"db_name"`
}
func (t *Tester) dropDB() bool {
dsn := t.DB.User + ":" + t.DB.Password + "@" + "tcp(" + t.DB.Host + ":" + strconv.Itoa(t.DB.Port) + ")/" + t.DB.DbName + "?charset=utf8"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println(err)
}
defer db.Close()
q := "DROP DATABASE " + t.DB.DbName + ";"
_, err = db.Exec(q)
if err != nil {
fmt.Println(err)
}
fmt.Println("------ Dropped CCTV Database -------")
return true
}
func (t *Tester) configureDatabase(DataBase *MysqlDB) bool {
var db MysqlDB
db.Host = t.Cameradar.DbHost
db.Port = t.Cameradar.DbPort
db.User = t.Cameradar.DbUser
db.Password = t.Cameradar.DbPassword
db.DbName = t.Cameradar.DbName
*DataBase = db
return true
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"bufio"
"fmt"
"io"
)
// Launch it via goroutine
// Start read log of service
func readLog(service *Service, reader io.ReadCloser) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
str := scanner.Text()
if service.Console {
fmt.Printf("[%s] %s\n", service.Path, str)
}
fmt.Printf("%s\n", str)
service.Mutex.Lock()
service.Logs = append(service.Logs, str)
service.Mutex.Unlock()
}
if err := scanner.Err(); err != nil {
fmt.Printf("[%s] Service failed: %s\n", service.Path, err)
}
fmt.Printf("Logger of service: [%s] stopped\n", service.Path)
service.Active = false
}
+19 -5
View File
@@ -1,3 +1,17 @@
// Copyright 2016 Etix Labs
//
// 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.
package main package main
import ( import (
@@ -5,24 +19,24 @@ import (
) )
func main() { func main() {
manager := new(manager) Tester := new(Tester)
defer manager.Stop() defer Tester.Stop()
// Parse conf (streams should already be launched by Jenkins) // Parse conf (streams should already be launched by Jenkins)
fmt.Println("--- Initializing Cameradar Test Tool ... ---") fmt.Println("--- Initializing Cameradar Test Tool ... ---")
if !manager.Init() { if !Tester.Init() {
fmt.Println("-> Cameradar Test Tool initialization FAILED") fmt.Println("-> Cameradar Test Tool initialization FAILED")
return return
} }
// Run tests // Run tests
if !manager.Run() { if !Tester.Run() {
fmt.Println("-> Cameradar Test Tool FAILED") fmt.Println("-> Cameradar Test Tool FAILED")
} }
// Write results // Write results
fmt.Println("--- Writing results... ---") fmt.Println("--- Writing results... ---")
if !manager.WriteResults(*(manager.Result), manager.Config.Output) { if !Tester.WriteResults(*(Tester.Result), Tester.Output) {
fmt.Println("-> Write results FAILED") fmt.Println("-> Write results FAILED")
return return
} }
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"encoding/json"
"errors"
"fmt"
"os"
)
// Result contains the data of a Cameradar result, plus an error field in order to add error messages to the JUnit report
type Result struct {
Address string `json:"address"`
IDsFound bool `json:"ids_found"`
PathFound bool `json:"path_found"`
Password string `json:"password"`
Port int `json:"port"`
Route string `json:"route"`
ServiceName string `json:"service_name"`
Protocol string `json:"protocol"`
State string `json:"state"`
Username string `json:"username"`
Valid bool `json:"valid"`
Thumb string `json:"thumbnail_path"`
err error // in case of a fail, add a message
}
// Launch it via goroutine
// Start read log of service
func getResult(test *[]Result, resultPath string) bool {
// Load config
resultFile, err := os.Open(resultPath)
if err != nil {
fmt.Printf("\nCan't open result file: %s\n", err)
return false
}
dec := json.NewDecoder(resultFile)
if err = dec.Decode(&test); err != nil {
fmt.Printf("\nUnable to deserialize result file: %s\n", err)
return false
}
return true
}
func isValid(e Result, r Result) bool {
if e.Username != r.Username {
e.err = errors.New(e.Address + " had a different username than " + r.Username)
return false
}
if e.Password != r.Password {
e.err = errors.New(e.Address + " had a different password than " + r.Password)
return false
}
if e.Port != r.Port {
e.err = errors.New(e.Address + " had a different port than expected")
return false
}
if e.Valid != r.Valid {
e.err = errors.New(e.Address + " had a different validity than expected")
return false
}
return true
}
// Extend needs refacto
func Extend(slice []Result, element Result) []Result {
n := len(slice)
if n == cap(slice) {
// Slice is full; must grow.
// We double its size and add 1, so if the size is zero we still grow.
newSlice := make([]Result, len(slice), 2*len(slice)+1)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0 : n+1]
slice[n] = element
return slice
}
+26 -14
View File
@@ -1,3 +1,17 @@
// Copyright 2016 Etix Labs
//
// 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.
package main package main
import ( import (
@@ -9,22 +23,21 @@ import (
// Service needs refacto // Service needs refacto
type Service struct { type Service struct {
Path string `json:"Path"` Path string `json:"path"`
Args string `json:"Args"` Args string `json:"args"`
Ports string `json:"Ports"` Ports string `json:"ports"`
IdsPath string `json:"IdsPath"` IdsPath string `json:"ids_path"`
RoutesPath string `json:"RoutesPath"` RoutesPath string `json:"routes_path"`
DbHost string `json:"dbHost"` ThumbPath string `json:"thumb_path"`
DbPort int `json:"dbPort"` DbHost string `json:"db_host"`
DbUser string `json:"dbUser"` DbPort int `json:"db_port"`
DbPassword string `json:"dbPassword"` DbUser string `json:"db_user"`
DbName string `json:"dbName"` DbPassword string `json:"db_password"`
ThumbPath string `json:"ThumbPath"` DbName string `json:"db_name"`
Console bool `json:"Console"` Console bool `json:"console"`
Logs []string Logs []string
Active bool // Based on io.ReadCloser status Active bool // Based on io.ReadCloser status
Mutex sync.Mutex Mutex sync.Mutex
cmd *exec.Cmd // Go handler of the service cmd *exec.Cmd // Go handler of the service
} }
@@ -77,7 +90,6 @@ func killService(service *Service) {
fmt.Println(err) fmt.Println(err)
} }
// Sending SIGABORT, more reliable for VLC
sigAbort := []string{service.Path, "-s", "SIGABRT"} sigAbort := []string{service.Path, "-s", "SIGABRT"}
fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Path) fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Path)
cmd = exec.Command("killall", sigAbort...) cmd = exec.Command("killall", sigAbort...)
+46 -132
View File
@@ -1,173 +1,87 @@
// Copyright 2016 Etix Labs
//
// 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.
package main package main
import ( import (
"encoding/json" "errors"
"fmt" "fmt"
"io/ioutil"
"net"
"os"
"sync" "sync"
"time" "time"
) )
// MysqlDB needs refacto // Test represents a test launched with Cameradar
type MysqlDB struct { type Test struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
DbName string `json:"db_name"`
}
// CameradarConfig needs refacto
type CameradarConfig struct {
MysqlDB MysqlDB `json:"mysql_db"`
Subnets string `json:"subnets"`
Ports string `json:"ports"`
RtspURLFile string `json:"rtsp_url_file"`
RtspIdsFile string `json:"rtsp_ids_file"`
ThumbnailStoragePath string `json:"thumbnail_storage_path"`
}
// Result needs refacto
type Result struct {
Address string `json:"address"`
Password string `json:"password"`
Port string `json:"port"`
Route string `json:"route"`
Username string `json:"username"`
Valid bool `json:"valid,omitempty"`
Thumb string `json:"thumbnail_path,omitempty"`
}
// TestCase needs refacto
type TestCase struct {
expected []Result expected []Result
result []Result result []Result
time time.Duration time time.Duration
ok bool
} }
// Invoke the test // Invoke the test
// Wrap results in a TestResult object // Wrap results in a TestResult object
func (m *manager) invokeTestCase(testCase *TestCase, wg *sync.WaitGroup) { func (t *Tester) invokeTestCase(testCase *Test, wg *sync.WaitGroup) {
startTime := time.Now() startTime := time.Now()
if m.runTestCase(testCase) { t.runTestCase(testCase)
testCase.time = time.Since(startTime) testCase.time = time.Since(startTime)
testCase.ok = true
fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds()) fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds())
} else {
testCase.time = time.Since(startTime)
testCase.ok = false
fmt.Printf("Test failed in %.6fs\n", testCase.time.Seconds())
}
wg.Done() wg.Done()
} }
func (m *manager) runTestCase(test *TestCase) bool { // Checks all valid results that are supposed to match
fmt.Printf("Test triggered\n") // Adds them to the valid results and leave the failed
// ones in the expected slice
Cameradar := m.Config.Cameradar //
startService(&Cameradar) // Then, if the result did not match the expected but it was supposed to fail
// Add it to the valid results and remove it from the expected slice
for Cameradar.Active { func (t *Tester) runTestCase(test *Test) {
time.Sleep(5 * time.Millisecond) startService(&t.Cameradar)
for t.Cameradar.Active {
time.Sleep(25 * time.Millisecond)
} }
found := 0
toFind := len(test.expected)
var validResults []Result var validResults []Result
if getResult(&test.result, "result.json") { if getResult(&test.result, "/tmp/shared/result.json") {
// Check all valid resutls that are supposed to match
// Add them to the valid results and leave the failed
// ones in the expected slice
for _, r := range test.result { for _, r := range test.result {
index := 0
r.Valid = true r.Valid = true
for _, e := range test.expected { for index, e := range test.expected {
e.Thumb = r.Thumb if e.Address == r.Address && isValid(e, r) {
var err error // _, err := os.Stat(r.Thumb)
var addr []string // if err == nil) {
addr, err = net.LookupHost(e.Address)
e.Address = addr[0]
if e == r {
_, err = os.Stat(r.Thumb)
if err == nil {
fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.") fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.")
found++
validResults = Extend(validResults, r) validResults = Extend(validResults, r)
if len(test.expected) > 1 {
test.expected = append(test.expected[:index], test.expected[index+1:]...) test.expected = append(test.expected[:index], test.expected[index+1:]...)
}
break break
} else { // } else {
fmt.Println("The result of ", r.Address, " seemed valid, but the thumbnails could not be generated by Cameradar.") // e.err = error{"The result of " + e.Address + " seemed valid, but the thumbnails could not be generated by Cameradar : " + err.Error()}
// }
} }
} }
index++
} }
} for index, e := range test.expected {
index := 0
// If the result did not match the expected but it was supposed to fail
// Add it to the valid results and remove it from the expected slice
for _, e := range test.expected {
if !e.Valid { if !e.Valid {
found++ fmt.Println("The result of", e.Address, "successfully failed.")
validResults = Extend(validResults, e) validResults = Extend(validResults, e)
if len(test.expected) > 1 {
test.expected = append(test.expected[:index], test.expected[index+1:]...) test.expected = append(test.expected[:index], test.expected[index+1:]...)
break
} }
index++ } else {
e.err = errors.New("The camera with the address " + e.Address + " was not found by cameradar")
fmt.Println("Should have been valid but was not found : ", e.Address)
} }
// If we found all the expected results, return true
if found == toFind {
return true
} }
test.result = validResults test.result = validResults
} }
return false
}
func (m *manager) generateConfig(test []Result, DataBase *mysql_db) bool {
var config CameradarConfig
var db mysql_db
db.Host = m.Config.Cameradar.DbHost
db.Port = m.Config.Cameradar.DbPort
db.User = m.Config.Cameradar.DbUser
db.Password = m.Config.Cameradar.DbPassword
db.Db_name = m.Config.Cameradar.DbName
for _, t := range test {
if len(config.Subnets) > 0 {
config.Subnets += ","
}
config.Subnets += t.Address
}
config.Mysql_db = db
config.Ports = m.Config.Cameradar.Ports
config.Rtsp_url_file = m.Config.Cameradar.RoutesPath
config.Rtsp_ids_file = m.Config.Cameradar.IdsPath
config.Thumbnail_storage_path = m.Config.Cameradar.ThumbPath
b, _ := json.Marshal(config)
fmt.Println(string(b))
err := ioutil.WriteFile("tmp_config", b, 0644)
if err != nil {
fmt.Println(err)
return false
}
*DataBase = db
return true
}
// Extend needs refacto
func Extend(slice []Result, element Result) []Result {
n := len(slice)
if n == cap(slice) {
// Slice is full; must grow.
// We double its size and add 1, so if the size is zero we still grow.
newSlice := make([]Result, len(slice), 2*len(slice)+1)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0 : n+1]
slice[n] = element
return slice
} }
+67
View File
@@ -0,0 +1,67 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"fmt"
"sync"
)
// Tester is the structure that will manage the whole testing
type Tester struct {
Cameradar Service `json:"cameradar"`
Output string
Tests []Result
Result *Test
DB MysqlDB
}
// Init gets the testing configuration and makes sure that no other Cameradar service is running at the moment
func (t *Tester) Init() bool {
fmt.Println("- Parsing")
if !t.parseConfig() {
return false
}
fmt.Println("- Cleaning content")
killService(&t.Cameradar)
return true
}
// Run launches the tests that have been set up by the init method
func (t *Tester) Run() bool {
var wg sync.WaitGroup
fmt.Println("\n- Launching all tests")
var newTest = new(Test)
newTest.expected = t.Tests
if t.configureDatabase(&t.DB) {
t.dropDB()
wg.Add(1)
go t.invokeTestCase(newTest, &wg)
t.Result = newTest
}
wg.Wait()
fmt.Println("All tests completed")
return true
}
// Stop kills the service launched by the tester
func (t *Tester) Stop() bool {
killService(&t.Cameradar)
return true
}
+51 -48
View File
@@ -1,11 +1,26 @@
// Copyright 2016 Etix Labs
//
// 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.
package main package main
import ( import (
"bytes"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"time"
) )
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -14,20 +29,22 @@ import (
// JUnitTestSuites is a collection of JUnit test suites. // JUnitTestSuites is a collection of JUnit test suites.
type JUnitTestSuites struct { type JUnitTestSuites struct {
XMLName xml.Name `xml:"testsuites"` XMLName xml.Name `xml:"testsuites"`
Suites []JUnitTestSuite TestSuites []JUnitTestSuite `xml:"testsuite"`
} }
// JUnitTestSuite is a single JUnit test suite which may contain many // JUnitTestSuite is a single JUnit test suite which may contain many
// testcases. // testcases.
type JUnitTestSuite struct { type JUnitTestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Tests int `xml:"tests,attr"` Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"` Failures int `xml:"failures,attr"`
Time string `xml:"time,attr"` Time string `xml:"time,attr"`
TestCases []JUnitTestCase TestCases []JUnitTestCase `xml:"testcase"`
} }
// JUnitTestCase is a single test case with its result. // JUnitTestCase is a single test case with its result.
type JUnitTestCase struct { type JUnitTestCase struct {
XMLName xml.Name `xml:"testcase"`
Message string `xml:"message,attr"` Message string `xml:"message,attr"`
Time string `xml:"time,attr"` Time string `xml:"time,attr"`
Failure *JUnitFailure `xml:"failure,omitempty"` Failure *JUnitFailure `xml:"failure,omitempty"`
@@ -35,25 +52,25 @@ type JUnitTestCase struct {
// JUnitFailure contains data related to a failed test. // JUnitFailure contains data related to a failed test.
type JUnitFailure struct { type JUnitFailure struct {
XMLName xml.Name `xml:"failure"`
Message string `xml:"message,attr"` Message string `xml:"message,attr"`
Type string `xml:"type,attr"` Type string `xml:"type,attr"`
Contents string `xml:",chardata"` Contents string `xml:",chardata"`
} }
func (m *manager) WriteResults(result TestCase, output string) bool { // WriteResults will output the results in the standard output as well as concatenate them in an XML JUnit report
func (t *Tester) WriteResults(result Test, output string) bool {
fmt.Printf("Displaying results...\n") fmt.Printf("Displaying results...\n")
// Write Console report t.writeConsoleReport(result)
m.writeConsoleReport(result)
// Write XML report
// Open xml
file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644) file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644)
if err != nil { if err != nil {
fmt.Printf("Error opening XML: %s\n", err) fmt.Printf("Error opening XML: %s\n", err)
return false return false
} }
defer file.Close() defer file.Close()
err = m.writeJUnitReportXML(result, file, output)
err = t.writeJUnitReportXML(result, file, output)
if err != nil { if err != nil {
fmt.Printf("Error writing XML: %s\n", err) fmt.Printf("Error writing XML: %s\n", err)
return false return false
@@ -63,42 +80,47 @@ func (m *manager) WriteResults(result TestCase, output string) bool {
} }
// Write tests results under JUnit format on w // Write tests results under JUnit format on w
func (m *manager) writeJUnitReportXML(result TestCase, r io.ReadWriter, output string) error { func (t *Tester) writeJUnitReportXML(result Test, rw io.ReadWriter, output string) error {
suites := JUnitTestSuites{} suites := JUnitTestSuites{}
dec := xml.NewDecoder(r)
if err := dec.Decode(&suites); err != nil { buf, err := ioutil.ReadFile(output)
fmt.Printf("\nUnable to deserialize XML log file: %s\n", err)
dec := xml.NewDecoder(bytes.NewBufferString(string(buf)))
err = dec.Decode(&suites)
if err != nil {
fmt.Printf("\nUnable to deserialize %s file: %s\n", output, err)
} }
ts := JUnitTestSuite{ ts := JUnitTestSuite{
Tests: len(result.result) + len(result.expected), Tests: len(result.result) + len(result.expected),
Failures: 0, Failures: 0,
Time: fmt.Sprintf("%.6f", result.time.Seconds()), Time: fmt.Sprintf("%.6f", result.time.Seconds()),
TestCases: []JUnitTestCase{}, TestCases: []JUnitTestCase{},
} }
// Run throught all iterations
for _, r := range result.result {
testCase := JUnitTestCase{ testCase := JUnitTestCase{
Time: fmt.Sprintf("%.6f", result.time.Seconds()), Time: fmt.Sprintf("%.6f", result.time.Seconds()),
Failure: nil, Failure: nil,
} }
if len(result.result) > 0 { testCase.Message = "The stream " + r.Address + " could be accessed and its thumbnail was properly generated"
testCase.Message = "These streams matched what we expected:" ts.TestCases = append(ts.TestCases, testCase)
} }
for _, success := range result.result {
testCase.Message += " " + success.Address for _, e := range result.expected {
testCase := JUnitTestCase{
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
Failure: nil,
} }
if !result.ok { if e.err != nil {
testCase.Failure = &JUnitFailure{ testCase.Failure = &JUnitFailure{
Message: "These streams did not match what we expected:", Message: e.err.Error(),
Type: "", Type: "",
} }
} }
for _, fail := range result.expected {
ts.Failures++
testCase.Failure.Message += " " + fail.Address
} }
ts.TestCases = append(ts.TestCases, testCase)
suites.Suites = append(suites.Suites, ts) suites.TestSuites = append(suites.TestSuites, ts)
// Fix indent // Fix indent
bytes, err := xml.MarshalIndent(suites, "", "\t") bytes, err := xml.MarshalIndent(suites, "", "\t")
if err != nil { if err != nil {
@@ -112,35 +134,16 @@ func (m *manager) writeJUnitReportXML(result TestCase, r io.ReadWriter, output s
} }
writer := io.Writer(w) writer := io.Writer(w)
writer.Write(bytes) writer.Write(bytes)
return nil return nil
} }
func (m *manager) writeConsoleReport(result TestCase) bool { func (t *Tester) writeConsoleReport(result Test) bool {
min := 50 * time.Hour successCount := len(result.result)
max := 0 * time.Second failureCount := len(result.expected)
total := 0 * time.Second
successCount := 0
failureCount := 0
if result.ok {
successCount++
total += result.time
if result.time < min {
min = result.time
}
if result.time > max {
max = result.time
}
} else {
failureCount++
}
fmt.Println("--- Test summary ---") fmt.Println("--- Test summary ---")
if successCount > 0 { if successCount > 0 {
fmt.Printf("Results: %d/%d (%d%%)\n", successCount, successCount+failureCount, successCount*100/(successCount+failureCount)) fmt.Printf("Results: %d/%d (%d%%)\n", successCount, successCount+failureCount, successCount*100/(successCount+failureCount))
fmt.Printf("Total time: %.6fs\n", total.Seconds()) fmt.Printf("Time: %.6fs\n", result.time.Seconds())
fmt.Printf("Average time: %.6fs\n", total.Seconds()/float64(successCount))
fmt.Printf("Min time: %.6fs\n", min.Seconds())
fmt.Printf("Max time: %.6fs\n", max.Seconds())
} else { } else {
fmt.Printf("No test in success\n") fmt.Printf("No test in success\n")
} }