Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
slapos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Justin
slapos
Commits
4aa27252
Commit
4aa27252
authored
Sep 30, 2022
by
Léo-Paul Géneau
👾
Committed by
Justin
Feb 08, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
software/js-drone: Add test
parent
c4096489
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
351 additions
and
0 deletions
+351
-0
software/js-drone/test/README.md
software/js-drone/test/README.md
+1
-0
software/js-drone/test/setup.py
software/js-drone/test/setup.py
+50
-0
software/js-drone/test/test.py
software/js-drone/test/test.py
+293
-0
software/slapos-sr-testing/software.cfg
software/slapos-sr-testing/software.cfg
+7
-0
No files found.
software/js-drone/test/README.md
0 → 100644
View file @
4aa27252
Tests for js-drone Software Release
software/js-drone/test/setup.py
0 → 100644
View file @
4aa27252
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from
setuptools
import
setup
,
find_packages
version
=
'0.0.1.dev0'
name
=
'slapos.test.js_drone'
with
open
(
"README.md"
)
as
f
:
long_description
=
f
.
read
()
setup
(
name
=
name
,
version
=
version
,
description
=
"Test for SlapOS' js-drone"
,
long_description
=
long_description
,
long_description_content_type
=
'text/markdown'
,
maintainer
=
"Nexedi"
,
maintainer_email
=
"info@nexedi.com"
,
url
=
"https://lab.nexedi.com/nexedi/slapos"
,
packages
=
find_packages
(),
install_requires
=
[
'slapos.core'
,
'slapos.libnetworkcache'
,
'erp5.util'
],
zip_safe
=
True
,
test_suite
=
'test'
,
)
software/js-drone/test/test.py
0 → 100644
View file @
4aa27252
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import
ipaddress
import
json
import
os
import
socket
import
struct
import
subprocess
import
time
from
slapos.testing.testcase
import
makeModuleSetUpAndTestCaseClass
MAIN_SCRIPT_NAME
=
'main.js'
'''
0. positionArray
0.1 latitude
0.2 longitude
0.3 absolute altitude
0.4 relative altitude
1. speedArray
1.1 yaw angle
1.2 air speed
1.3 climb rate
2. message
'''
MONITORED_ITEM_NB
=
3
OPC_UA_PORT
=
4840
OPC_UA_NET_IF
=
'lo'
MCAST_GRP
=
'ff15::1111'
USER_SCRIPT_NAME
=
'user.js'
# OPC UA Pub/Sub related constants
VERSION
=
1
PUBLISHER_ID_ENABLED
=
True
GROUP_HEADER_ENABLED
=
True
PAYLOAD_HEADER_ENABLED
=
True
EXTENDED_FLAGS1_ENABLED
=
True
PUBLISHER_ID_TYPE
=
1
#UInt16
DATA_SET_CLASS_ID_ENABLED
=
False
SECURITY_ENABLED
=
False
NETWORK_MESSAGE_TIMESTAMP_ENABLED
=
False
PICOSECONDS_ENABLED
=
False
EXTENDED_FLAGS2_ENABLED
=
False
PUBLISHER_ID
=
0
WRITER_GROUP_ID_ENABLED
=
True
GROUP_VERSION_ENABLED
=
False
NETWORK_MESSAGE_NUMBER_ENABLED
=
False
SEQUENCE_NUMBER_ENABLED
=
False
WRITER_GROUP_ID
=
1
DATASET_PAYLOAD_HEADER_COUNT
=
1
DATA_SET_WRITER_ID
=
(
1
,)
DATA_SET_MESSAGE_VALIDE
=
1
FIELD_ENCODING
=
0
#UA_FIELDENCODING_VARIANT
DATA_SET_MESSAGE_SEQUENCE_NR_ENABLED
=
False
STATUS_ENABLED
=
False
CONFIG_VERSION_MAJOR_VERSION_ENABLED
=
True
CONFIG_VERSION_MINOR_VERSION_ENABLED
=
True
MESSAGE_HEADER_FLAGS2_ENABLED
=
True
DATA_SET_MESSAGE_TYPE
=
0
#UA_DATASETMESSAGE_DATAKEYFRAME
DATA_SET_MESSAGE_TIMESTAMP_ENABLED
=
True
PICO_SECONDS_INCLUDED
=
False
UA_DATETIME_SEC
=
10000000
UA_DATETIME_UNIX_EPOCH
=
11644473600
*
UA_DATETIME_SEC
CONFIG_VERSION_MAJOR_VERSION
=
1690792766
CONFIG_VERSION_MINOR_VERSION
=
1690781976
POSITION_ARRAY_TYPE
=
11
#double
POSITION_ARRAY_VALUES
=
(
45.64
,
14.25
,
686.61
,
91.24
)
SPEED_ARRAY_TYPE
=
10
#float
SPEED_ARRAY_VALUES
=
(
-
72.419998
,
15.93
,
-
0.015
)
STRING_TYPE
=
12
TEST_MESSAGE
=
b'{"content":"{
\
\
"next_checkpoint
\
\
":1}","dest_id":-1}'
setUpModule
,
SlapOSInstanceTestCase
=
makeModuleSetUpAndTestCaseClass
(
os
.
path
.
abspath
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
'..'
,
'software.cfg'
)))
class
JSDroneTestCase
(
SlapOSInstanceTestCase
):
@
classmethod
def
getInstanceParameterDict
(
cls
):
return
{
'_'
:
json
.
dumps
({
'netIf'
:
OPC_UA_NET_IF
,
'subscriberGuidList'
:
[
cls
.
slap
.
_computer_id
],
})
}
def
get_partition
(
self
,
instance_type
):
software_url
=
self
.
getSoftwareURL
()
for
computer_partition
in
self
.
slap
.
computer
.
getComputerPartitionList
():
partition_url
=
computer_partition
.
getSoftwareRelease
().
_software_release
partition_type
=
computer_partition
.
getType
()
if
partition_url
==
software_url
and
partition_type
==
instance_type
:
return
computer_partition
raise
Exception
(
"JS-drone %s partition not found"
%
instance_type
)
def
setUp
(
self
):
super
().
setUp
()
subscriber_partition
=
self
.
get_partition
(
'drone'
)
instance_path
=
json
.
loads
(
subscriber_partition
.
getConnectionParameterDict
()[
'_'
])[
'instance-path'
]
quickjs_bin
=
os
.
path
.
join
(
instance_path
,
'bin'
,
'qjs'
)
script_dir
=
os
.
path
.
join
(
instance_path
,
'etc'
)
self
.
qjs_process
=
subprocess
.
Popen
(
[
quickjs_bin
,
os
.
path
.
join
(
script_dir
,
MAIN_SCRIPT_NAME
),
os
.
path
.
join
(
script_dir
,
USER_SCRIPT_NAME
),
],
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
)
time
.
sleep
(
0.1
)
def
tearDown
(
self
):
if
self
.
qjs_process
.
returncode
==
None
:
self
.
qjs_process
.
kill
()
self
.
qjs_process
.
communicate
()
super
().
tearDown
()
def
ua_networkMessage_encodeHeader
(
self
):
ua_byte1
=
int
(
VERSION
)
ua_byte1
|=
int
(
PUBLISHER_ID_ENABLED
)
<<
4
ua_byte1
|=
int
(
GROUP_HEADER_ENABLED
)
<<
5
ua_byte1
|=
int
(
PAYLOAD_HEADER_ENABLED
)
<<
6
ua_byte1
|=
int
(
EXTENDED_FLAGS1_ENABLED
)
<<
7
ua_byte2
=
PUBLISHER_ID_TYPE
ua_byte2
|=
int
(
DATA_SET_CLASS_ID_ENABLED
)
<<
3
ua_byte2
|=
int
(
SECURITY_ENABLED
)
<<
4
ua_byte2
|=
int
(
NETWORK_MESSAGE_TIMESTAMP_ENABLED
)
<<
5
ua_byte2
|=
int
(
PICOSECONDS_ENABLED
)
<<
6
ua_byte2
|=
int
(
EXTENDED_FLAGS2_ENABLED
)
<<
7
return
struct
.
pack
(
'BBH'
,
ua_byte1
,
ua_byte2
,
PUBLISHER_ID
)
def
ua_groupHeader_encode
(
self
):
ua_byte
=
int
(
WRITER_GROUP_ID_ENABLED
)
ua_byte
|=
int
(
GROUP_VERSION_ENABLED
)
<<
1
ua_byte
|=
int
(
NETWORK_MESSAGE_NUMBER_ENABLED
)
<<
2
ua_byte
|=
int
(
SEQUENCE_NUMBER_ENABLED
)
<<
3
return
struct
.
pack
(
'B'
,
ua_byte
)
+
struct
.
pack
(
'H'
,
WRITER_GROUP_ID
)
def
ua_payloadHeader_encode
(
self
):
header
=
struct
.
pack
(
'B'
,
DATASET_PAYLOAD_HEADER_COUNT
)
for
data_set_writer_id
in
DATA_SET_WRITER_ID
:
header
+=
struct
.
pack
(
'H'
,
data_set_writer_id
)
return
header
def
ua_networkMessage_encodeHeaders
(
self
):
headers
=
self
.
ua_networkMessage_encodeHeader
()
headers
+=
self
.
ua_groupHeader_encode
()
headers
+=
self
.
ua_payloadHeader_encode
()
return
headers
def
ua_dataSetMessageHeader_encode
(
self
):
ua_byte1
=
int
(
DATA_SET_MESSAGE_VALIDE
)
ua_byte1
|=
FIELD_ENCODING
<<
1
ua_byte1
|=
int
(
DATA_SET_CLASS_ID_ENABLED
)
<<
3
ua_byte1
|=
int
(
STATUS_ENABLED
)
<<
4
ua_byte1
|=
int
(
CONFIG_VERSION_MAJOR_VERSION_ENABLED
)
<<
5
ua_byte1
|=
int
(
CONFIG_VERSION_MINOR_VERSION_ENABLED
)
<<
6
ua_byte1
|=
int
(
MESSAGE_HEADER_FLAGS2_ENABLED
)
<<
7
ua_byte2
=
DATA_SET_MESSAGE_TYPE
ua_byte2
|=
int
(
DATA_SET_MESSAGE_TIMESTAMP_ENABLED
)
<<
4
ua_byte2
|=
int
(
PICO_SECONDS_INCLUDED
)
<<
5
header
=
struct
.
pack
(
'BB'
,
ua_byte1
,
ua_byte2
)
ua_datetime
=
time
.
time
()
*
UA_DATETIME_SEC
+
UA_DATETIME_UNIX_EPOCH
return
header
+
struct
.
pack
(
'qII'
,
int
(
ua_datetime
),
CONFIG_VERSION_MAJOR_VERSION
,
CONFIG_VERSION_MINOR_VERSION
)
def
ua_array_encode
(
self
,
type_id
,
struct_type
,
values
):
encoding_mask
=
type_id
encoding_mask
+=
1
<<
7
# is an array
ua_array
=
struct
.
pack
(
'B'
,
encoding_mask
)
ua_array
+=
struct
.
pack
(
'I'
,
len
(
values
))
for
value
in
values
:
ua_array
+=
struct
.
pack
(
struct_type
,
value
)
return
ua_array
def
ua_dataSetMessage_encode
(
self
):
data_set_message
=
self
.
ua_dataSetMessageHeader_encode
()
data_set_message
+=
struct
.
pack
(
'H'
,
MONITORED_ITEM_NB
)
data_set_message
+=
self
.
ua_array_encode
(
POSITION_ARRAY_TYPE
,
'd'
,
POSITION_ARRAY_VALUES
,
)
data_set_message
+=
self
.
ua_array_encode
(
SPEED_ARRAY_TYPE
,
'f'
,
SPEED_ARRAY_VALUES
,
)
data_set_message
+=
struct
.
pack
(
'B'
,
STRING_TYPE
)
data_set_message
+=
struct
.
pack
(
'I'
,
len
(
TEST_MESSAGE
))
data_set_message
+=
TEST_MESSAGE
return
data_set_message
def
send_ua_networkMessage
(
self
):
ua_message
=
self
.
ua_networkMessage_encodeHeaders
()
ua_message
+=
self
.
ua_dataSetMessage_encode
()
with
socket
.
socket
(
socket
.
AF_INET6
,
socket
.
SOCK_DGRAM
,
socket
.
IPPROTO_UDP
)
as
s
:
s
.
setsockopt
(
socket
.
IPPROTO_IP
,
socket
.
IP_MULTICAST_TTL
,
2
)
s
.
sendto
(
ua_message
,
(
'::1'
,
OPC_UA_PORT
))
def
test_requested_instances
(
self
):
connection_parameter_dict
=
json
.
loads
(
self
.
computer_partition
.
getConnectionParameterDict
()[
'_'
])
self
.
assertEqual
(
connection_parameter_dict
[
'drone-id-list'
],
[])
self
.
assertEqual
(
connection_parameter_dict
[
'subscriber-id-list'
],
[
0
])
def
test_subscriber_instance_parameter_dict
(
self
):
self
.
assertEqual
(
json
.
loads
(
self
.
get_partition
(
'drone'
).
getInstanceParameterDict
()[
'_'
]),
{
'autopilotIp'
:
'192.168.27.1'
,
'id'
:
0
,
'isASimulation'
:
False
,
'isADrone'
:
False
,
'flightScript'
:
'https://lab.nexedi.com/nexedi/flight-scripts/raw/master/subscribe.js'
,
'multicastIp'
:
MCAST_GRP
,
'numberOfPeers'
:
1
,
'netIf'
:
OPC_UA_NET_IF
}
)
def
net_if_name_to_index
(
self
,
name
):
with
open
(
'/sys/class/net/%s/ifindex'
%
name
)
as
f
:
index
=
int
(
f
.
readline
())
return
index
def
test_joined_multicast_grp
(
self
):
expected_string
=
'%s %s %s 1 00000004 0
\
n
'
%
(
self
.
net_if_name_to_index
(
OPC_UA_NET_IF
),
OPC_UA_NET_IF
,
ipaddress
.
IPv6Address
(
MCAST_GRP
).
exploded
.
replace
(
':'
,
''
),
)
with
open
(
'/proc/net/igmp6'
)
as
f
:
self
.
assertIn
(
expected_string
,
f
.
readlines
())
def
test_pubsub_subscription
(
self
):
self
.
send_ua_networkMessage
()
time
.
sleep
(
0.1
)
outs
,
_
=
self
.
qjs_process
.
communicate
(
b'q
\
n
'
,
timeout
=
15
)
decoded_out
=
outs
.
decode
()
for
line
in
(
'Subscription 0 | MonitoredItem %s'
%
MONITORED_ITEM_NB
,
'Received position of drone 0: %f° %f° %fm %fm'
%
POSITION_ARRAY_VALUES
,
'Received speed of drone 0: %f° %fm/s %fm/s'
%
SPEED_ARRAY_VALUES
,
'Received message for drone 0: %s'
%
TEST_MESSAGE
.
decode
(),
):
self
.
assertIn
(
line
,
decoded_out
)
software/slapos-sr-testing/software.cfg
View file @
4aa27252
...
...
@@ -252,6 +252,11 @@ setup = ${slapos-repository:location}/software/mosquitto/test/
egg = slapos.test.peertube
setup = ${slapos-repository:location}/software/peertube/test/
[slapos.test.js-drone-setup]
<= setup-develop-egg
egg = slapos.test.js_drone
setup = ${slapos-repository:location}/software/js-drone/test/
[slapos.core-repository]
<= git-clone-repository
repository = https://lab.nexedi.com/nexedi/slapos.core.git
...
...
@@ -316,6 +321,7 @@ eggs +=
${slapos.test.html5as-setup:egg}
${slapos.test.htmlvalidatorserver-setup:egg}
${slapos.test.hugo-setup:egg}
${slapos.test.js-drone-setup:egg}
${slapos.test.jscrawler-setup:egg}
${slapos.test.jstestnode-setup:egg}
${slapos.test.jupyter-setup:egg}
...
...
@@ -407,6 +413,7 @@ tests =
html5as-base ${slapos.test.html5as-base-setup:setup}
htmlvalidatorserver ${slapos.test.htmlvalidatorserver-setup:setup}
hugo ${slapos.test.hugo-setup:setup}
js-drone ${slapos.test.js-drone-setup:setup}
jscrawler ${slapos.test.jscrawler-setup:setup}
jstestnode ${slapos.test.jstestnode-setup:setup}
jupyter ${slapos.test.jupyter-setup:setup}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment