Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c44b933a83 | |||
| 1f5e9fc502 | |||
| 08231074b9 | |||
| c6d801750e | |||
| 2cf49a8db4 | |||
| 4fba8a8594 | |||
| 76365e3a07 | |||
| e6a38af241 | |||
| a4ad49c1a7 |
+9
-9
@@ -19,9 +19,9 @@ set (PROJECT_NAME cameradar)
|
||||
|
||||
project (${PROJECT_NAME})
|
||||
|
||||
set (${PROJECT_NAME}_VERSION_MAJOR 0)
|
||||
set (${PROJECT_NAME}_VERSION_MINOR 1)
|
||||
set (${PROJECT_NAME}_VERSION_PATCH 1)
|
||||
set (${PROJECT_NAME}_VERSION_MAJOR 1)
|
||||
set (${PROJECT_NAME}_VERSION_MINOR 0)
|
||||
set (${PROJECT_NAME}_VERSION_PATCH 0)
|
||||
set (${PROJECT_NAME}_SUFFIX "-beta")
|
||||
set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}")
|
||||
|
||||
@@ -92,10 +92,10 @@ link_directories (
|
||||
)
|
||||
|
||||
include_directories (
|
||||
"cameradar_standalone/include"
|
||||
"deps/jsoncpp/src/deps.jsoncpp/include"
|
||||
"deps/boost/src/deps.boost/include"
|
||||
"deps/mysql-connector/include"
|
||||
"cameradar_standalone/include"
|
||||
"deps/jsoncpp/src/deps.jsoncpp/include"
|
||||
"deps/boost/src/deps.boost/include"
|
||||
"deps/mysql-connector/include"
|
||||
)
|
||||
|
||||
set (${CAMERADAR_BINARIES} "")
|
||||
@@ -122,8 +122,8 @@ set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${${PROJECT_NAME}_VERSION}_${CMAKE
|
||||
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||
set (CPACK_PACKAGE_VERSION_MAJOR "0")
|
||||
set (CPACK_PACKAGE_VERSION_MINOR "1")
|
||||
set (CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set (CPACK_PACKAGE_VERSION_MINOR "2")
|
||||
set (CPACK_PACKAGE_VERSION_PATCH "2")
|
||||
set (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}_${${PROJECT_NAME}_VERSION}")
|
||||
set (CPACK_GENERATOR "TGZ")
|
||||
set (CPACK_SOURCE_GENERATOR "TGZ")
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## An RTSP surveillance camera access multitool
|
||||
|
||||
[](#license)
|
||||
[](https://github.com/EtixLabs/cameradar/releases/latest)
|
||||
|
||||
|
||||
#### Cameradar allows you to:
|
||||
|
||||
* **Detect open RTSP hosts** on any accessible subnetwork
|
||||
@@ -42,7 +46,7 @@ The quick install uses docker to build Cameradar without polluting your machine
|
||||
|
||||
### Dependencies
|
||||
|
||||
The only dependencies are `docker` and `docker-compose`.
|
||||
The only dependencies are `docker`, `docker-tools`, `git` and `make`.
|
||||
|
||||
### Five steps guide
|
||||
|
||||
@@ -63,6 +67,7 @@ The manual installation is recommended if you want to tweak Cameradar and quickl
|
||||
To install Cameradar you will need these packages
|
||||
|
||||
* cmake (`cmake`)
|
||||
* git (`git`)
|
||||
* gstreamer1.x (`libgstreamer1.0-dev`)
|
||||
* ffmpeg (`ffmpeg`)
|
||||
* libcurl (`libcurl4-openssl-dev`)
|
||||
@@ -100,6 +105,13 @@ The only dependencies are `docker` and `docker-compose`.
|
||||
Here is the basic content of the configuration file with simple placeholders :
|
||||
```json
|
||||
{
|
||||
"mysql_db" : {
|
||||
"host" : "MYSQL_SERVER_IP_ADDRESS",
|
||||
"port" : MYSQL_SERVER_PORT,
|
||||
"user": "root",
|
||||
"password": "root",
|
||||
"db_name": "cmrdr"
|
||||
},
|
||||
"subnets" : "SUBNET1,SUBNET2,SUBNET3,[...]",
|
||||
"ports" : "PORT1,PORT2,[...]",
|
||||
"rtsp_url_file" : "conf/url.json",
|
||||
|
||||
@@ -21,5 +21,4 @@ set (LIBRARY_OUTPUT_PATH ${CAMERADAR_CACHE_MANAGER_OUTPUT_PATH})
|
||||
add_subdirectory(dumb_cache_manager)
|
||||
add_subdirectory(mysql_cache_manager)
|
||||
|
||||
message (${CAMERADAR_CACHE_MANAGERS})
|
||||
set (CAMERADAR_CACHE_MANAGERS ${CAMERADAR_CACHE_MANAGERS} PARENT_SCOPE)
|
||||
|
||||
@@ -53,8 +53,10 @@ dumb_cache_manager::set_streams(std::vector<etix::cameradar::stream_model> model
|
||||
//! Inserts a single stream to the cache
|
||||
void
|
||||
dumb_cache_manager::update_stream(const etix::cameradar::stream_model& newmodel) {
|
||||
for (auto& it : this->streams) {
|
||||
if (it.address == newmodel.address && it.port == newmodel.port) { it = newmodel; }
|
||||
for (auto& stream : this->streams) {
|
||||
if (stream.address == newmodel.address && stream.port == newmodel.port) {
|
||||
stream = newmodel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +64,9 @@ dumb_cache_manager::update_stream(const etix::cameradar::stream_model& newmodel)
|
||||
std::vector<etix::cameradar::stream_model>
|
||||
dumb_cache_manager::get_streams() {
|
||||
std::vector<stream_model> ret;
|
||||
for (const auto& it : this->streams) {
|
||||
if (not it.service_name.compare("rtsp") && not it.state.compare("open")) ret.push_back(it);
|
||||
for (const auto& stream : this->streams) {
|
||||
if (not stream.service_name.compare("rtsp") && not stream.state.compare("open"))
|
||||
ret.push_back(stream);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -72,10 +75,10 @@ dumb_cache_manager::get_streams() {
|
||||
std::vector<etix::cameradar::stream_model>
|
||||
dumb_cache_manager::get_valid_streams() {
|
||||
std::vector<stream_model> ret;
|
||||
for (const auto& it : this->streams) {
|
||||
if ((not it.service_name.compare("rtsp") && not it.state.compare("open")) && it.ids_found &&
|
||||
it.path_found)
|
||||
ret.push_back(it);
|
||||
for (const auto& stream : this->streams) {
|
||||
if ((not stream.service_name.compare("rtsp") && not stream.state.compare("open")) &&
|
||||
stream.ids_found && stream.path_found)
|
||||
ret.push_back(stream);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
// if you're not familiar with Docker, only change the
|
||||
// cache_manager_name value
|
||||
"cache_manager_path" : "../cache_managers",
|
||||
"cache_manager_name" : "mysql"
|
||||
"cache_manager_name" : "dumb"
|
||||
}
|
||||
|
||||
@@ -71,12 +71,15 @@ curl_describe(const std::string& path, bool logs) {
|
||||
curl_easy_cleanup(csession);
|
||||
fclose(protofile);
|
||||
curl_global_cleanup();
|
||||
LOG_DEBUG_("Response code : " + std::to_string(rc), "describe");
|
||||
if (logs) {
|
||||
if (rc != 401 && pos == std::string::npos)
|
||||
// Some cameras return 400 instead of 401, don't know why.
|
||||
// Some cameras timeout and then curl considers the status as 0
|
||||
if (rc != 401 && rc != 400 && rc && pos == std::string::npos)
|
||||
LOG_INFO_("Unprotected camera discovered.", "brutelogs");
|
||||
return ((res == CURLE_OK) && rc != 401);
|
||||
return ((res == CURLE_OK) && rc != 401 && rc != 400 && rc);
|
||||
}
|
||||
return ((res == CURLE_OK) && rc != 404);
|
||||
return ((res == CURLE_OK) && rc != 404 && rc != 400 && rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -6,8 +6,8 @@ cameradar:
|
||||
- "./conf:/tmp/conf:ro"
|
||||
- "./cameradar_thumbnails:/tmp/cameradar_thumbnails"
|
||||
links:
|
||||
- ext_cctv_mysql
|
||||
ext_cctv_mysql:
|
||||
- mysql
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
|
||||
+7
-1
@@ -27,12 +27,18 @@ echo -e $COL_GREEN"ok"$COL_RESET
|
||||
# container. The container has to be linked in docker-compose.yml for cameradar
|
||||
# to be able to interact with it.
|
||||
echo -n "replacing mysql host and port in configuration "
|
||||
sed -i s#__MYSQL_ADDR__#ext_cctv_mysql#g $CONF
|
||||
sed -i s#__MYSQL_ADDR__#mysql#g $CONF
|
||||
|
||||
# Reaplce 3306 with the port of your DB
|
||||
sed -i s#__MYSQL_PORT__#3306#g $CONF
|
||||
echo -e $COL_GREEN"ok"$COL_RESET
|
||||
|
||||
echo -n "waiting for mysql to be ready "
|
||||
while ! mysqladmin ping -h"mysql" -P3306 --silent; do
|
||||
sleep 1
|
||||
done
|
||||
echo -e $COL_GREEN"ok"$COL_RESET
|
||||
|
||||
/cameradar/bin/cameradar -l 1 -c /conf/cameradar.conf.json &
|
||||
cameradar_pid=$!
|
||||
|
||||
|
||||
Vendored
+1
@@ -17,6 +17,7 @@ cmake_minimum_required (VERSION 2.8.1)
|
||||
# Lib subdirectory
|
||||
|
||||
include (jsoncpp)
|
||||
include (boost)
|
||||
include (mysql_connector)
|
||||
|
||||
set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM ON)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
FROM ubuntu:15.10
|
||||
|
||||
MAINTAINER brendan.leglaunec@etixgroup.com
|
||||
|
||||
ENV LD_LIBRARY_PATH="/cctv/libraries"
|
||||
|
||||
# install go
|
||||
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 tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
# set variable env
|
||||
ENV GOPATH=/go
|
||||
ENV PATH=$PATH:/go/bin
|
||||
ENV PATH=$PATH:/usr/local/go/bin
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
|
||||
# needed for cameradar
|
||||
RUN apt-get update && apt-get install -y \
|
||||
nmap \
|
||||
libmysqlclient18 \
|
||||
ffmpeg \
|
||||
mysql-client \
|
||||
libgstreamer1.0-dev \
|
||||
gstreamer1.0-plugins-base \
|
||||
gstreamer1.0-plugins-good \
|
||||
libcurl4-openssl-dev
|
||||
|
||||
RUN apt-get install -y psmisc
|
||||
|
||||
ADD cctv_*_Debug_Linux.tar.gz /
|
||||
RUN mv cctv_*_Debug_Linux cctv
|
||||
|
||||
# create cameradaratest folder in go src path
|
||||
RUN mkdir -p /go/src/cameradartest
|
||||
ADD ./conf /conf
|
||||
ADD ./docker/run_cameradartest.sh /run.sh
|
||||
|
||||
# get go deps
|
||||
RUN go get github.com/go-sql-driver/mysql
|
||||
|
||||
RUN mkdir /thumbnails
|
||||
WORKDIR /go/src/cameradartest
|
||||
CMD ["/run.sh"]
|
||||
@@ -0,0 +1,14 @@
|
||||
FROM ubuntu:15.10
|
||||
MAINTAINER brendan.leglaunec@etixgroup.com
|
||||
|
||||
RUN useradd -m vlc; \
|
||||
apt-get update; \
|
||||
apt-get install -y vlc-nox
|
||||
|
||||
RUN sed -i s/geteuid/getppid/g /usr/bin/vlc
|
||||
|
||||
ADD ./docker/screen.png /vlc/screen.png
|
||||
COPY ./docker/run_vlc.sh /start.sh
|
||||
COPY ./etix_rtsp_server /etix_rtsp_server
|
||||
|
||||
EXPOSE 8554
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"mysql_db" : {
|
||||
"host" : "0.0.0.0",
|
||||
"port" : 3306,
|
||||
"user": "root",
|
||||
"password": "root",
|
||||
"db_name": "cctv"
|
||||
},
|
||||
"subnets" : "172.16.100.13 localhost",
|
||||
"ports" : "554,8554", // if not specified, default will be 1-65535
|
||||
"rtsp_url_file" : "conf/url.json",
|
||||
"rtsp_ids_file" : "conf/ids.json",
|
||||
"thumbnail_storage_path" : "/ce/que/tu/veux"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"Output": "cameratest.log.xml",
|
||||
|
||||
"Cameradar" : {
|
||||
"Path": "/home/ullaakut/Work/cctv_server2/cameradar/test/cameradar",
|
||||
"Args": "-l 1 -c tmp_config",
|
||||
"Ports": "554,5554,8554",
|
||||
"IdsPath": "conf/ids.json",
|
||||
"RoutesPath": "conf/url.json",
|
||||
"ThumbPath": "/home/ullaakut/.cctv",
|
||||
"dbHost": "0.0.0.0",
|
||||
"dbPort": 3306,
|
||||
"dbUser": "root",
|
||||
"dbPassword": "root",
|
||||
"dbName": "cctv",
|
||||
"Console": false
|
||||
},
|
||||
"Tests" : [
|
||||
{
|
||||
"address" : "127.0.0.1",
|
||||
"password" : "",
|
||||
"port" : "8554",
|
||||
"route" : "live.sdp",
|
||||
"username" : "",
|
||||
"valid" : true
|
||||
},
|
||||
{
|
||||
"address" : "172.16.100.11",
|
||||
"password" : "",
|
||||
"port" : "553",
|
||||
"route" : "live.sdp",
|
||||
"username" : "admin",
|
||||
"valid" : false
|
||||
},
|
||||
{
|
||||
"address" : "172.16.100.13",
|
||||
"password" : "",
|
||||
"port" : "554",
|
||||
"route" : "live.sdp",
|
||||
"username" : "admin",
|
||||
"valid" : true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"username": [
|
||||
"",
|
||||
"admin",
|
||||
"Admin",
|
||||
"root",
|
||||
"supervisor",
|
||||
"ubnt"
|
||||
],
|
||||
"password" : [
|
||||
"",
|
||||
"admin",
|
||||
"9999",
|
||||
"123456",
|
||||
"pass",
|
||||
"camera",
|
||||
"1234",
|
||||
"12345",
|
||||
"fliradmin",
|
||||
"system",
|
||||
"jvc",
|
||||
"meinsm",
|
||||
"root",
|
||||
"4321",
|
||||
"1111111",
|
||||
"password",
|
||||
"ikwd",
|
||||
"supervisor",
|
||||
"ubnt"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"urls" : [
|
||||
"/",
|
||||
"/1.AMP",
|
||||
"/1/stream1",
|
||||
"/CAM_ID.password.mp2",
|
||||
"/GetData.cgi",
|
||||
"/MediaInput/h264",
|
||||
"/MediaInput/mpeg4",
|
||||
"/VideoInput/1/h264/1",
|
||||
"/access_code",
|
||||
"/access_name_for_stream_1_to_5",
|
||||
"/av0_0",
|
||||
"/av2",
|
||||
"/avn=2",
|
||||
"/axis-media/media.amp",
|
||||
"/cam",
|
||||
"/cam0_0",
|
||||
"/cam0_1",
|
||||
"/cam1/h264",
|
||||
"/cam1/h264/multicast",
|
||||
"/cam1/mjpeg",
|
||||
"/cam1/mpeg4",
|
||||
"/camera.stm",
|
||||
"/ch0",
|
||||
"/ch001.sdp",
|
||||
"/ch0_unicast_firststream",
|
||||
"/ch0_unicast_secondstream",
|
||||
"/channel1",
|
||||
"/h264",
|
||||
"/h264/media.amp",
|
||||
"/image.mpg",
|
||||
"/img/media.sav",
|
||||
"/img/video.asf",
|
||||
"/img/video.sav",
|
||||
"/ioImage/1",
|
||||
"/ipcam.sdp",
|
||||
"/ipcam_h264.sdp",
|
||||
"/live.sdp",
|
||||
"/live/h264",
|
||||
"/live/mpeg4",
|
||||
"/live_mpeg4.sdp",
|
||||
"/livestream",
|
||||
"/livestream/",
|
||||
"/media/media.amp",
|
||||
"/media/video1",
|
||||
"/mjpeg/media.smp",
|
||||
"/mp4",
|
||||
"/mpeg4",
|
||||
"/mpeg4/1/media.amp",
|
||||
"/mpeg4/media.amp",
|
||||
"/mpeg4/media.smp",
|
||||
"/mpeg4unicast",
|
||||
"/mpg4/rtsp.amp",
|
||||
"/multicaststream",
|
||||
"/now.mp4",
|
||||
"/nph-h264.cgi",
|
||||
"/nphMpeg4/g726-640x",
|
||||
"/nphMpeg4/g726-640x480",
|
||||
"/nphMpeg4/nil-320x240",
|
||||
"/play1.sdp",
|
||||
"/play2.sdp",
|
||||
"/rtpvideo1.sdp",
|
||||
"/rtsp_tunnel",
|
||||
"/rtsph264",
|
||||
"/stream1",
|
||||
"/user.pin.mp2",
|
||||
"/user_defined",
|
||||
"/video",
|
||||
"/video.3gp",
|
||||
"/video.mp4",
|
||||
"/video1",
|
||||
"/video1+audio1",
|
||||
"/vis",
|
||||
"/wfov"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"Output": "cameratest.log.xml",
|
||||
"Cameradar" : {
|
||||
"Path": "/cctv/bin/cameradar",
|
||||
"Args": "-l 1 -c tmp_config",
|
||||
"Ports": "554,5554,8554,5548",
|
||||
"IdsPath": "/conf/ids.json",
|
||||
"RoutesPath": "/conf/url.json",
|
||||
"ThumbPath": "/thumbnails",
|
||||
"dbHost": "mysql_cameradar",
|
||||
"dbPort": 3306,
|
||||
"dbUser": "root",
|
||||
"dbPassword": "root",
|
||||
"dbName": "cctv",
|
||||
"Console": false
|
||||
},
|
||||
"Tests" : __CAMERAS__
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"username": [
|
||||
"",
|
||||
"admin",
|
||||
"Admin",
|
||||
"root",
|
||||
"supervisor",
|
||||
"ubnt"
|
||||
],
|
||||
"password" : [
|
||||
"",
|
||||
"admin",
|
||||
"9999",
|
||||
"123456",
|
||||
"pass",
|
||||
"camera",
|
||||
"1234",
|
||||
"12345",
|
||||
"fliradmin",
|
||||
"system",
|
||||
"jvc",
|
||||
"meinsm",
|
||||
"root",
|
||||
"4321",
|
||||
"1111111",
|
||||
"password",
|
||||
"ikwd",
|
||||
"supervisor",
|
||||
"ubnt"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"urls" : [
|
||||
"/",
|
||||
"/1.AMP",
|
||||
"/1/stream1",
|
||||
"/CAM_ID.password.mp2",
|
||||
"/GetData.cgi",
|
||||
"/MediaInput/h264",
|
||||
"/MediaInput/mpeg4",
|
||||
"/VideoInput/1/h264/1",
|
||||
"/access_code",
|
||||
"/access_name_for_stream_1_to_5",
|
||||
"/av0_0",
|
||||
"/av2",
|
||||
"/avn=2",
|
||||
"/axis-media/media.amp",
|
||||
"/cam",
|
||||
"/cam0_0",
|
||||
"/cam0_1",
|
||||
"/cam1/h264",
|
||||
"/cam1/h264/multicast",
|
||||
"/cam1/mjpeg",
|
||||
"/cam1/mpeg4",
|
||||
"/camera.stm",
|
||||
"/ch0",
|
||||
"/ch001.sdp",
|
||||
"/ch0_unicast_firststream",
|
||||
"/ch0_unicast_secondstream",
|
||||
"/channel1",
|
||||
"/h264",
|
||||
"/h264/media.amp",
|
||||
"/image.mpg",
|
||||
"/img/media.sav",
|
||||
"/img/video.asf",
|
||||
"/img/video.sav",
|
||||
"/ioImage/1",
|
||||
"/ipcam.sdp",
|
||||
"/ipcam_h264.sdp",
|
||||
"/live.sdp",
|
||||
"/live/h264",
|
||||
"/live/mpeg4",
|
||||
"/live_mpeg4.sdp",
|
||||
"/livestream",
|
||||
"/livestream/",
|
||||
"/media/media.amp",
|
||||
"/media/video1",
|
||||
"/mjpeg/media.smp",
|
||||
"/mp4",
|
||||
"/mpeg4",
|
||||
"/mpeg4/1/media.amp",
|
||||
"/mpeg4/media.amp",
|
||||
"/mpeg4/media.smp",
|
||||
"/mpeg4unicast",
|
||||
"/mpg4/rtsp.amp",
|
||||
"/multicaststream",
|
||||
"/now.mp4",
|
||||
"/nph-h264.cgi",
|
||||
"/nphMpeg4/g726-640x",
|
||||
"/nphMpeg4/g726-640x480",
|
||||
"/nphMpeg4/nil-320x240",
|
||||
"/play1.sdp",
|
||||
"/play2.sdp",
|
||||
"/rtpvideo1.sdp",
|
||||
"/rtsp_tunnel",
|
||||
"/rtsph264",
|
||||
"/stream1",
|
||||
"/user.pin.mp2",
|
||||
"/user_defined",
|
||||
"/video",
|
||||
"/video.3gp",
|
||||
"/video.mp4",
|
||||
"/video1",
|
||||
"/video1+audio1",
|
||||
"/vis",
|
||||
"/wfov"
|
||||
]
|
||||
}
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
|
||||
ports=('8554' '8554' '8554' '8554' '8554' '8554')
|
||||
users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '')
|
||||
passwords=('admin' 'root' '12345' 'ubnt' 'password' '')
|
||||
routes=('live.sdp' 'live.sdp' 'ch001.sdp' '' 'invalid' 'live_mpeg4.sdp')
|
||||
cams_name_pattern="fake_camera_"
|
||||
|
||||
# json generation variable only
|
||||
json="[\n"
|
||||
first=true
|
||||
# $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid
|
||||
function make_json {
|
||||
if [ "$first" = true ] ; then first=false
|
||||
else json="$json,\n"; fi
|
||||
json="$json{"
|
||||
json="$json\"address\":\"$1\","
|
||||
json="$json\"port\":\"$2\","
|
||||
json="$json\"route\":\"$3\","
|
||||
json="$json\"username\":\"$4\","
|
||||
json="$json\"password\":\"$5\","
|
||||
json="$json\"valid\":$6"
|
||||
json="$json}"
|
||||
}
|
||||
|
||||
# $1 = configuration template path
|
||||
function generate_conf {
|
||||
echo "generate configuration"
|
||||
sed s#__CAMERAS__#$json#g $1 > cameradartest.conf.json
|
||||
}
|
||||
|
||||
# $1 = numbers of cameras to generate
|
||||
function start {
|
||||
# Seed random generator
|
||||
RANDOM=$(date +%s)
|
||||
|
||||
# start cameras
|
||||
for (( i=1; i<=$1; i++ )); do
|
||||
name="$cams_name_pattern$i"
|
||||
# random conf
|
||||
conf_idx=$(($RANDOM % ${#ports[@]}))
|
||||
|
||||
# get conf variables
|
||||
port=${ports[$conf_idx]}
|
||||
user=${users[$conf_idx]}
|
||||
passw=${passwords[$conf_idx]}
|
||||
route=${routes[$conf_idx]}
|
||||
is_valid=true
|
||||
|
||||
# if conf_idx = 4 -> invalid conf
|
||||
if [ "$conf_idx" == "4" ] ; then is_valid=false; fi
|
||||
|
||||
docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route"
|
||||
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid
|
||||
done
|
||||
|
||||
# finalize json
|
||||
json="$json]"
|
||||
echo "$json"
|
||||
}
|
||||
|
||||
function stop {
|
||||
# if no cameras containers are started just exit
|
||||
camera_count="`docker ps -a -q --filter="name=$cams_name_pattern" | wc -l`"
|
||||
if [ "$camera_count" == "0" ]; then
|
||||
echo "error: no cameras started"; exit 1
|
||||
fi
|
||||
|
||||
echo "stopping and removing $camera_count containers"
|
||||
# docker stop $(docker ps -a -q --filter="name=$cams_name_pattern")
|
||||
docker rm -f $(docker ps -a -q --filter="name=$cams_name_pattern") > /dev/null
|
||||
}
|
||||
|
||||
# need first argument at least
|
||||
if [ "$1" == "" ]; then
|
||||
echo "error: invalid number of argument"
|
||||
exit 1
|
||||
fi
|
||||
case $1 in
|
||||
"start")
|
||||
# check if the argument is a number.
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $2 =~ $re ]] ; then
|
||||
echo "error: argument is not a number"; exit 1
|
||||
fi
|
||||
if [[ "$3" == "" ]] ; then
|
||||
echo "error: missing path to the configuration file template"; exit 1
|
||||
fi
|
||||
echo "starting $2 cameras"
|
||||
start $2
|
||||
generate_conf $3
|
||||
;;
|
||||
"stop")
|
||||
echo "stopping all cameras tests"
|
||||
stop
|
||||
;;
|
||||
"help")
|
||||
echo "./gen_cameras.sh start CAMS_NB - start CAMS_NB cameras"
|
||||
echo " stop - stop all started cameras"
|
||||
echo " help - display this help"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "invalid test name"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
while ! mysqladmin ping -h"mysql_cameradar" -P3306 --silent; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
ls -alhR /conf
|
||||
cat /etc/hosts
|
||||
|
||||
# build
|
||||
go build
|
||||
# run test
|
||||
./cameradartest /tmp/tests/cameradartest.conf.json
|
||||
|
||||
cp cameratest.log.xml /tmp/tests/
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/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.
|
After Width: | Height: | Size: 558 KiB |
Executable
BIN
Binary file not shown.
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# check if a debug package exist in the current folder
|
||||
if ! ls ./cctv_*_Debug_Linux.tar.gz 1> /dev/null 2>&1; then
|
||||
(echo "no debug package in the current folder"; exit 137)
|
||||
exit 137
|
||||
fi
|
||||
|
||||
cams_name_pattern="fake_camera_"
|
||||
cmd=""
|
||||
|
||||
function make_docker_command {
|
||||
cmd="docker run --rm"
|
||||
|
||||
# start cameras
|
||||
for (( i=1; i<=$1; i++ )); do
|
||||
name="$cams_name_pattern$i"
|
||||
cmd="$cmd --link=\"$name\""
|
||||
done
|
||||
|
||||
# add mysql libk
|
||||
cmd="$cmd --link=\"mysql_cameradar\""
|
||||
# add cameradar srcs
|
||||
cmd="$cmd -v \"`pwd`/src:/go/src/cameradartest\""
|
||||
# add cmaeradar conf
|
||||
cmd="$cmd -v \"`pwd`/:/tmp/tests\""
|
||||
# add container name
|
||||
cmd="$cmd cameradartest"
|
||||
}
|
||||
|
||||
function start_test {
|
||||
make_docker_command $1
|
||||
./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json
|
||||
eval $cmd
|
||||
./docker/gen_cameras.sh stop
|
||||
}
|
||||
|
||||
# build images
|
||||
echo "building docker images"
|
||||
# building fake-camera container
|
||||
docker build -f Dockerfile-camera -t fake-camera .
|
||||
|
||||
# building cameradartest image
|
||||
docker build -t cameradartest .
|
||||
|
||||
# getting mysql
|
||||
echo "starting mysql"
|
||||
docker pull mysql:5.7
|
||||
docker run --name mysql_cameradar -e MYSQL_DATABASE=cctv -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
|
||||
|
||||
start_test 1
|
||||
start_test 5
|
||||
# start_test 10
|
||||
# start_test 20
|
||||
|
||||
# stop mysql
|
||||
echo "stopping mysql"
|
||||
docker rm -f mysql_cameradar
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (m *manager) parseConfig() bool {
|
||||
// Get config file path
|
||||
confPath := "conf/cameratest.conf.json"
|
||||
av := len(os.Args)
|
||||
if av == 2 {
|
||||
confPath = os.Args[1]
|
||||
}
|
||||
|
||||
// Load config
|
||||
fmt.Printf("Loading config file: %s ... ", confPath)
|
||||
configFile, err := os.Open(confPath)
|
||||
if err != nil {
|
||||
fmt.Printf("\nCan't open config file: %s\n", err)
|
||||
return false
|
||||
}
|
||||
dec := json.NewDecoder(configFile)
|
||||
if err = dec.Decode(&m); err != nil {
|
||||
fmt.Printf("\nUnable to deserialize config file: %s\n", err)
|
||||
return false
|
||||
}
|
||||
fmt.Println("Configuration file successfully loaded\n")
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (m *manager) dropDB() bool {
|
||||
dsn := m.DB.User + ":" + m.DB.Password + "@" + "tcp(" + m.DB.Host + ":" + strconv.Itoa(m.DB.Port) + ")/" + m.DB.Db_name + "?charset=utf8"
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer db.Close()
|
||||
q := "DROP DATABASE cctv;"
|
||||
_, err = db.Exec(q)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("------ Dropped CCTV Database -------")
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
manager := new(manager)
|
||||
defer manager.Stop()
|
||||
|
||||
// Parse conf (streams should already be launched by Jenkins)
|
||||
fmt.Println("--- Initializing Cameradar Test Tool ... ---")
|
||||
if !manager.Init() {
|
||||
fmt.Println("-> Cameradar Test Tool initialization FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
// Run tests
|
||||
if !manager.Run() {
|
||||
fmt.Println("-> Cameradar Test Tool FAILED")
|
||||
}
|
||||
|
||||
// Write results
|
||||
fmt.Println("--- Writing results... ---")
|
||||
if !manager.WriteResults(*(manager.Result), manager.Config.Output) {
|
||||
fmt.Println("-> Write results FAILED")
|
||||
return
|
||||
}
|
||||
fmt.Println("--- Writing results done ---")
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type manager struct {
|
||||
Config
|
||||
|
||||
Tests []Result
|
||||
Result *TestCase
|
||||
DB mysql_db
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Cameradar Service `json:"Cameradar"`
|
||||
|
||||
Output string
|
||||
}
|
||||
|
||||
func (m *manager) Init() bool {
|
||||
fmt.Println("- Parsing")
|
||||
if !m.parseConfig() {
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Println("- Cleaning content")
|
||||
killService(&m.Config.Cameradar)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *manager) Run() bool {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
fmt.Println("\n- Launching all tests")
|
||||
var newTest = new(TestCase)
|
||||
newTest.expected = m.Tests
|
||||
if (m.generateConfig(m.Tests, &m.DB)) {
|
||||
m.dropDB()
|
||||
wg.Add(1)
|
||||
go m.invokeTestCase(newTest, &wg)
|
||||
m.Result = newTest
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Printf("All tests completed\n")
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *manager) Stop() bool {
|
||||
killService(&m.Config.Cameradar)
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Path string `json:"Path"`
|
||||
Args string `json:"Args"`
|
||||
Ports string `json:"Ports"`
|
||||
IdsPath string `json:"IdsPath"`
|
||||
RoutesPath string `json:"RoutesPath"`
|
||||
DbHost string `json:"dbHost"`
|
||||
DbPort int `json:"dbPort"`
|
||||
DbUser string `json:"dbUser"`
|
||||
DbPassword string `json:"dbPassword"`
|
||||
DbName string `json:"dbName"`
|
||||
ThumbPath string `json:"ThumbPath"`
|
||||
Console bool `json:"Console"`
|
||||
|
||||
Logs []string
|
||||
Active bool // Based on io.ReadCloser status
|
||||
|
||||
Mutex sync.Mutex
|
||||
cmd *exec.Cmd // Go handler of the service
|
||||
}
|
||||
|
||||
func startService(service *Service) bool {
|
||||
// Launch service
|
||||
args := strings.Fields(service.Args)
|
||||
service.cmd = exec.Command(service.Path, args...)
|
||||
|
||||
handler, err := service.cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
errHandler, err := service.cmd.StderrPipe()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
// Launch
|
||||
err = service.cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Printf("Service: [%s] started\n", service.Path)
|
||||
service.Active = true
|
||||
|
||||
// Read service logs and update service status
|
||||
// Set pipes
|
||||
go readLog(service, handler)
|
||||
go readLog(service, errHandler)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Stop only specified service instance
|
||||
func stopService(service *Service) {
|
||||
service.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
// Kill all instances of specified service
|
||||
func killService(service *Service) {
|
||||
// Sending SIGTERM
|
||||
fmt.Printf("Executing: killall %s\n", service.Path)
|
||||
cmd := exec.Command("killall", service.Path)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Sending SIGABORT, more reliable for VLC
|
||||
sigAbort := []string{service.Path, "-s", "SIGABRT"}
|
||||
fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Path)
|
||||
cmd = exec.Command("killall", sigAbort...)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
"net"
|
||||
)
|
||||
|
||||
type mysql_db struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Db_name string `json:"db_name"`
|
||||
}
|
||||
|
||||
type CameradarConfig struct {
|
||||
Mysql_db mysql_db `json:"mysql_db"`
|
||||
Subnets string `json:"subnets"`
|
||||
Ports string `json:"ports"`
|
||||
Rtsp_url_file string `json:"rtsp_url_file"`
|
||||
Rtsp_ids_file string `json:"rtsp_ids_file"`
|
||||
Thumbnail_storage_path string `json:"thumbnail_storage_path"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
expected []Result
|
||||
result []Result
|
||||
time time.Duration
|
||||
ok bool
|
||||
}
|
||||
|
||||
// Invoke the test
|
||||
// Wrap results in a TestResult object
|
||||
func (m *manager) invokeTestCase(testCase *TestCase, wg *sync.WaitGroup) {
|
||||
startTime := time.Now()
|
||||
if m.runTestCase(testCase) {
|
||||
testCase.time = time.Since(startTime)
|
||||
testCase.ok = true
|
||||
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()
|
||||
}
|
||||
|
||||
func (m *manager) runTestCase(test *TestCase) bool {
|
||||
fmt.Printf("Test triggered\n")
|
||||
|
||||
Cameradar := m.Config.Cameradar
|
||||
startService(&Cameradar)
|
||||
|
||||
for Cameradar.Active {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
found := 0
|
||||
toFind := len(test.expected)
|
||||
var validResults []Result
|
||||
if getResult(&test.result, "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 {
|
||||
index := 0
|
||||
r.Valid = true
|
||||
for _, e := range test.expected {
|
||||
e.Thumb = r.Thumb
|
||||
var err error
|
||||
var addr[] string
|
||||
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.")
|
||||
found++
|
||||
validResults = Extend(validResults, r)
|
||||
test.expected = append(test.expected[:index], test.expected[index+1:]...)
|
||||
break
|
||||
} else {
|
||||
fmt.Println("The result of ", r.Address, " seemed valid, but the thumbnails could not be generated by Cameradar.")
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
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 {
|
||||
found++
|
||||
validResults = Extend(validResults, e)
|
||||
test.expected = append(test.expected[:index], test.expected[index+1:]...)
|
||||
break
|
||||
}
|
||||
index++
|
||||
}
|
||||
// If we found all the expected results, return true
|
||||
if found == toFind {
|
||||
return true
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Data declarations
|
||||
|
||||
// JUnitTestSuites is a collection of JUnit test suites.
|
||||
type JUnitTestSuites struct {
|
||||
XMLName xml.Name `xml:"testsuites"`
|
||||
Suites []JUnitTestSuite
|
||||
}
|
||||
|
||||
// JUnitTestSuite is a single JUnit test suite which may contain many
|
||||
// testcases.
|
||||
type JUnitTestSuite struct {
|
||||
Tests int `xml:"tests,attr"`
|
||||
Failures int `xml:"failures,attr"`
|
||||
Time string `xml:"time,attr"`
|
||||
TestCases []JUnitTestCase
|
||||
}
|
||||
|
||||
// JUnitTestCase is a single test case with its result.
|
||||
type JUnitTestCase struct {
|
||||
Message string `xml:"message,attr"`
|
||||
Time string `xml:"time,attr"`
|
||||
Failure *JUnitFailure `xml:"failure,omitempty"`
|
||||
}
|
||||
|
||||
// JUnitFailure contains data related to a failed test.
|
||||
type JUnitFailure struct {
|
||||
Message string `xml:"message,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Contents string `xml:",chardata"`
|
||||
}
|
||||
|
||||
func (m *manager) WriteResults(result TestCase, output string) bool {
|
||||
fmt.Printf("Displaying results...\n")
|
||||
// Write Console report
|
||||
m.writeConsoleReport(result)
|
||||
|
||||
// Write XML report
|
||||
// Open xml
|
||||
file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("Error opening XML: %s\n", err)
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
err = m.writeJUnitReportXML(result, file, output)
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing XML: %s\n", err)
|
||||
return false
|
||||
}
|
||||
fmt.Printf("-> JUnit XML report written: %s\n", output)
|
||||
return true
|
||||
}
|
||||
|
||||
// Write tests results under JUnit format on w
|
||||
func (m *manager) writeJUnitReportXML(result TestCase, r io.ReadWriter, output string) error {
|
||||
suites := JUnitTestSuites{}
|
||||
dec := xml.NewDecoder(r)
|
||||
if err := dec.Decode(&suites); err != nil {
|
||||
fmt.Printf("\nUnable to deserialize XML log file: %s\n", err)
|
||||
}
|
||||
ts := JUnitTestSuite{
|
||||
Tests: len(result.result) + len(result.expected),
|
||||
Failures: 0,
|
||||
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
|
||||
TestCases: []JUnitTestCase{},
|
||||
}
|
||||
// Run throught all iterations
|
||||
testCase := JUnitTestCase{
|
||||
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
|
||||
Failure: nil,
|
||||
}
|
||||
if len(result.result) > 0 {
|
||||
testCase.Message = "These streams matched what we expected:"
|
||||
}
|
||||
for _, success := range result.result {
|
||||
testCase.Message += " " + success.Address
|
||||
}
|
||||
if !result.ok {
|
||||
testCase.Failure = &JUnitFailure{
|
||||
Message: "These streams did not match what we expected:",
|
||||
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)
|
||||
// Fix indent
|
||||
bytes, err := xml.MarshalIndent(suites, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write in param stream
|
||||
|
||||
w, err := os.OpenFile(output, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
writer := io.Writer(w)
|
||||
writer.Write(bytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *manager) writeConsoleReport(result TestCase) bool {
|
||||
min := 50 * time.Hour
|
||||
max := 0 * time.Second
|
||||
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 ---\n")
|
||||
if successCount > 0 {
|
||||
fmt.Printf("Results: %d/%d (%d%%)\n", successCount, successCount+failureCount, successCount*100/(successCount+failureCount))
|
||||
fmt.Printf("Total time: %.6fs\n", total.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 {
|
||||
fmt.Printf("No test in success\n")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user