From 9f8e622237fe0d1360c216bda55d802c64146d5e Mon Sep 17 00:00:00 2001
From: Alain Takoudjou <alain.takoudjou@nexedi.com>
Date: Wed, 19 Jan 2022 11:42:09 +0100
Subject: [PATCH] Add Beremiz IDE software release

New software release for https://github.com/beremiz/beremiz.

IDE require X server with libvncserver and x11vnc to work and can be used
in a browser with deployed NoVNC.
Runtime deployed can be used to executes PLC program.

The software release is also used to run Beremiz IDE tests using sikuli and unittests
---
 software/beremiz-ide/buildout.hash.cfg        |  30 +++
 software/beremiz-ide/fluxbox-menu.in          |  22 ++
 .../instance-beremiz-test.cfg.jinja2.in       |  74 +++++
 .../instance-beremiz.cfg.jinja2.in            | 253 ++++++++++++++++++
 software/beremiz-ide/instance.cfg.in          |  84 ++++++
 software/beremiz-ide/software.cfg             | 205 ++++++++++++++
 software/beremiz-ide/test.cfg                 |  62 +++++
 software/beremiz-ide/test/README.md           |   2 +
 software/beremiz-ide/test/setup.py            |  50 ++++
 software/beremiz-ide/test/test.py             |  51 ++++
 10 files changed, 833 insertions(+)
 create mode 100644 software/beremiz-ide/buildout.hash.cfg
 create mode 100644 software/beremiz-ide/fluxbox-menu.in
 create mode 100644 software/beremiz-ide/instance-beremiz-test.cfg.jinja2.in
 create mode 100644 software/beremiz-ide/instance-beremiz.cfg.jinja2.in
 create mode 100644 software/beremiz-ide/instance.cfg.in
 create mode 100644 software/beremiz-ide/software.cfg
 create mode 100644 software/beremiz-ide/test.cfg
 create mode 100644 software/beremiz-ide/test/README.md
 create mode 100644 software/beremiz-ide/test/setup.py
 create mode 100644 software/beremiz-ide/test/test.py

diff --git a/software/beremiz-ide/buildout.hash.cfg b/software/beremiz-ide/buildout.hash.cfg
new file mode 100644
index 000000000..cf8c50da9
--- /dev/null
+++ b/software/beremiz-ide/buildout.hash.cfg
@@ -0,0 +1,30 @@
+# THIS IS NOT A BUILDOUT FILE, despite purposedly using a compatible syntax.
+# The only allowed lines here are (regexes):
+# - "^#" comments, copied verbatim
+# - "^[" section beginings, copied verbatim
+# - lines containing an "=" sign which must fit in the following categorie.
+#   - "^\s*filename\s*=\s*path\s*$" where "path" is relative to this file
+#     Copied verbatim.
+#   - "^\s*hashtype\s*=.*" where "hashtype" is one of the values supported
+#     by the re-generation script.
+#     Re-generated.
+# - other lines are copied verbatim
+# Substitution (${...:...}), extension ([buildout] extends = ...) and
+# section inheritance (< = ...) are NOT supported (but you should really
+# not need these here).
+
+[instance]
+filename = instance.cfg.in
+md5sum = c4079d70ab3268234651bf6c36234b2f
+
+[template-instance-beremiz]
+filename = instance-beremiz.cfg.jinja2.in
+md5sum = ac05663b9006b7c4c9810a18e1ea4220
+
+[template-instance-beremiz-test]
+filename = instance-beremiz-test.cfg.jinja2.in
+md5sum = 6049681908c5619d94499a6f4f224045
+
+[template-fluxbox-menu.in]
+filename = fluxbox-menu.in
+md5sum = 09560314eae0225b6085f8626f1a603a
diff --git a/software/beremiz-ide/fluxbox-menu.in b/software/beremiz-ide/fluxbox-menu.in
new file mode 100644
index 000000000..249da042e
--- /dev/null
+++ b/software/beremiz-ide/fluxbox-menu.in
@@ -0,0 +1,22 @@
+[begin] (Fluxbox)
+[encoding] {UTF-8}
+      [exec] (Beremiz IDE) { {{ beremiz_bin }} }
+      [exec] (Terminal) { {{ xterm_bin }} }
+[submenu] (Fluxbox menu)
+      [config] (Configure)
+[submenu] (System Styles) {Choose a style...}
+      [stylesdir] ({{ fluxbox_location }}/share/fluxbox/styles)
+[end]
+[submenu] (User Styles) {Choose a style...}
+      [stylesdir] (~/.fluxbox/styles)
+[end]
+      [workspaces] (Workspace List)
+      [commanddialog] (Fluxbox Command)
+      [reconfig] (Reload config)
+      [restart] (Restart)
+      [exec] (About) {(fluxbox -v; fluxbox -info | sed 1d) | xmessage -file - -center}
+      [separator]
+      [exit] (Exit)
+[end]
+[endencoding]
+[end]
diff --git a/software/beremiz-ide/instance-beremiz-test.cfg.jinja2.in b/software/beremiz-ide/instance-beremiz-test.cfg.jinja2.in
new file mode 100644
index 000000000..eb6c93f98
--- /dev/null
+++ b/software/beremiz-ide/instance-beremiz-test.cfg.jinja2.in
@@ -0,0 +1,74 @@
+[directory]
+tests = ${:srv}/tests
+
+[xserver]
+display-num = 42
+resolution = 1920x1080x24
+
+[wmctrl]
+recipe = slapos.cookbook:wrapper
+command-line = {{ wmctrl_bin }}
+wrapper-path = ${directory:bin}/wmctrl
+environment =
+  XORG_LOCK_DIR=${xserver:lock-dir}
+  DISPLAY=${xserver:display}
+
+[xdotool]
+recipe = slapos.cookbook:wrapper
+command-line = {{ xdotool_bin }}
+wrapper-path = ${directory:bin}/xdotool
+environment =
+  XORG_LOCK_DIR=${xserver:lock-dir}
+  DISPLAY=${xserver:display}
+
+[runTestSuite]
+env.sh  = ${beremiz-env.sh:output}
+workdir = {{ nxdtest_dir }}
+
+[beremiz-tests]
+recipe = slapos.recipe.template
+inline =
+  #!/bin/sh -e
+  cd {{ beremiz_location }}/tests
+  make test_dir=${directory:tests} xserver_command='echo "Using ${xserver:display} on Slapos X Server !";' "$@"
+output = ${directory:bin}/beremiztest
+
+[sikulix]
+recipe = slapos.cookbook:wrapper
+command-line = {{ sikulix_bin }} -v
+wrapper-path = ${directory:bin}/sikulix
+environment =
+  JAVA_TOOL_OPTIONS=-Duser.home=${buildout:directory} -Djava.io.tmpdir=${directory:tmp}
+
+[libopencv_java430.so]
+recipe = plone.recipe.command
+opencv-link = ${buildout:directory}/.Sikulix/SikulixLibs/libopencv_java430.so
+# We run sikulix with a random not existing projet so it will initialise all files
+# and extract libopencv_java430.so from jar. We can then replace it with slapos
+# compiled lib which solves GLIBC issues (version `GLIBC_2.27' not found).
+command =
+  LINK=${:opencv-link}
+  if [ ! -e "$LINK" ]; then
+    ${sikulix:wrapper-path} -r not_found_for_slapos.sikulix > /dev/null 2>&1 || true
+  fi
+  rm -f $LINK
+  ln -sf {{ opencv_location }}/lib/libopencv_java430.so $LINK
+update-command = ${:command}
+stop-on-error = true
+
+[buildout]
+extends =
+  {{ instance_nxdtest }}
+  {{ instance_beremiz }}
+
+parts +=
+  sikulix
+  wmctrl
+  xdotool
+  beremiz-tests
+  runTestSuite
+  libopencv_java430.so
+
+eggs-directory = {{ eggs_directory }}
+develop-eggs-directory = {{ develop_eggs_directory }}
+offline = true
diff --git a/software/beremiz-ide/instance-beremiz.cfg.jinja2.in b/software/beremiz-ide/instance-beremiz.cfg.jinja2.in
new file mode 100644
index 000000000..cab32bde4
--- /dev/null
+++ b/software/beremiz-ide/instance-beremiz.cfg.jinja2.in
@@ -0,0 +1,253 @@
+{% set part_list = [] -%}
+{% set ipv6 = (ipv6 | list)[0] -%}
+{% set ipv4 = (ipv4 | list)[0] -%}
+
+[directory]
+recipe = slapos.cookbook:mkdirectory
+etc = ${buildout:directory}/etc
+bin = ${buildout:directory}/bin
+srv = ${buildout:directory}/srv
+var = ${buildout:directory}/var
+tmp = ${buildout:directory}/tmp
+log = ${:var}/log
+vnc = ${buildout:directory}/.vnc
+scripts = ${:etc}/run
+services = ${:etc}/service
+promise = ${:etc}/promise
+ssl = ${:etc}/ssl
+auth = ${:tmp}/auth
+workdir = ${:srv}/workdir
+framebuffer = ${:srv}/framebuffer
+fluxbox = ${buildout:directory}/.fluxbox
+
+[gen-certificate]
+recipe = plone.recipe.command
+command = "{{ openssl_bin }}" req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout "${:key-file}" -out "${:cert-file}"
+stop-on-error = true
+cert-file = ${directory:ssl}/beremiz.crt
+key-file = ${directory:ssl}/beremiz.key
+
+[novnc-instance]
+recipe = slapos.cookbook:novnc
+path = ${directory:bin}/novnc
+ip = {{ ipv6 }}
+port = 6080
+vnc-ip = {{ ipv4 }}
+vnc-port = ${x11vnc:port}
+novnc-location = {{ novnc_location }}
+websockify-path = {{ websockify_bin }}
+ssl-key-path = ${gen-certificate:key-file}
+ssl-cert-path = ${gen-certificate:cert-file}
+
+[websockify-sighandler]
+recipe = slapos.cookbook:signalwrapper
+wrapper-path = ${directory:bin}/websockify-sighandler
+wrapped-path = ${novnc-instance:path}
+
+[websockify-sighandler-service]
+recipe = slapos.cookbook:wrapper
+command-line = ${websockify-sighandler:wrapper-path}
+wrapper-path = ${directory:services}/websockify
+hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
+
+[novnc-promise]
+<= monitor-promise-base
+promise = check_socket_listening
+name = novnc_promise.py
+config-host = ${novnc-instance:ip}
+config-port = ${novnc-instance:port}
+
+[x11vnc]
+recipe = slapos.cookbook:wrapper
+port = 5901
+command-line = {{ x11vnc_bin }} -forever -display ${xserver:display} -ncache 10
+  -noxdamage -rfbport ${:port} -no6 -noipv6 -reopen -o ${directory:log}/x11vnc.log
+  -usepw -rfbauth ${x11vnc-passwd:passfile} -desktop BeremizVNC
+  -listen {{ ipv4 }} -xkb
+wrapper-path = ${directory:services}/x11vnc
+hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
+environment =
+  XORG_LOCK_DIR=${directory:run}
+wait-for-files =
+  ${xserver:lock-file}
+
+[x11vnc-listen-promise]
+<= monitor-promise-base
+promise = check_socket_listening
+name = x11vnc_listening.py
+config-host = {{ ipv4 }}
+config-port = ${x11vnc:port}
+
+[random-password]
+recipe = slapos.cookbook:generate.password
+storage-path = ${directory:etc}/.passwd
+bytes = 8
+
+[x11vnc-passwd]
+recipe = slapos.recipe.template
+passfile = ${directory:vnc}/passwd
+inline =
+  #!/bin/sh -e
+  if [ -s "${:passfile}" ]; then
+    echo "Password initialized.";
+  else
+    chmod 700 $(dirname ${:passfile});
+    {{ x11vnc_bin }} -storepasswd ${random-password:passwd} ${:passfile};
+  fi
+output = ${directory:bin}/x11vnc_passwd
+mode = 700
+
+[generate-vnc-password]
+recipe = plone.recipe.command
+stop-on-error = true
+command = ${x11vnc-passwd:output}
+update-command = ${:command}
+
+# Generate a fonts.conf file.
+[font-config]
+recipe = slapos.recipe.template:jinja2
+url = {{ font_config_tmplt }}
+output = ${directory:etc}/fonts.conf
+context =
+  key cachedir :cache-dir
+  key fonts :fonts
+  key includes :includes
+cache-dir =
+  ${directory:etc}/.fontconfig.cache
+fonts =
+  {{ font_dejavu }}
+  {{ font_liberation }}
+includes =
+  {{ font_config_loc }}/etc/fonts/conf.d
+
+[xserver]
+recipe = slapos.cookbook:wrapper
+command-line = {{ xvfb_bin }} ${:display} -screen 0 ${:resolution}x24
+  -fbdir ${directory:framebuffer}
+wrapper-path = ${directory:services}/xserver
+hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
+environment =
+  XORG_LOCK_DIR=${:lock-dir}
+  FONTCONFIG_FILE=${font-config:output}
+display-num = 0
+display = :${:display-num}
+resolution = {{ slapparameter_dict.get('screen-resolution', '1280x1024') }}
+lock-dir = ${directory:run}
+lock-file = ${:lock-dir}/.X${:display-num}-lock
+
+[xserver-promise]
+recipe = slapos.cookbook:wrapper
+wrapper-path = ${directory:promise}/xserver-is-up
+command-line = bash -c "[ -S ${xserver:lock-dir}/.X11-unix/X${xserver:display-num} ]"
+
+[fluxbox-menu]
+recipe = slapos.recipe.template:jinja2
+url = {{ fluxbox_menu }}
+output = ${directory:fluxbox}/menu
+context =
+  key beremiz_bin      beremiz-x11:output
+  key xterm_bin        xterm:output
+  raw fluxbox_location {{ fluxbox_location }}
+
+[fluxbox-toolbar]
+recipe = plone.recipe.command
+command =
+  if [ ! -s "${directory:fluxbox}/init" ]; then
+    echo "session.screen0.toolbar.placement:      TopCenter" > ${directory:fluxbox}/init;
+  fi
+
+[fluxbox-bin]
+recipe = slapos.cookbook:wrapper
+command-line = {{ fluxbox_location }}/bin/fluxbox -display ${xserver:display}
+wrapper-path = ${directory:bin}/fluxbox
+environment =
+  HOME=${buildout:directory}
+  XORG_LOCK_DIR=${xserver:lock-dir}
+  FONTCONFIG_FILE=${font-config:output}
+  LANG=C.UTF-8
+  LC_ALL=C.UTF-8
+
+depends =
+  ${fluxbox-menu:recipe}
+  ${fluxbox-toolbar:recipe}
+
+[fluxbox]
+recipe = slapos.cookbook:wrapper
+command-line = ${fluxbox-bin:wrapper-path} -log ${directory:log}/fluxbox.log
+wrapper-path = ${directory:services}/fluxbox
+
+[beremiz-env.sh]
+recipe = slapos.recipe.template
+inline =
+  export LD_LIBRARY_PATH={{ mesa_location }}/lib
+  export PATH=${directory:bin}:{{ git_bin_dir }}:{{ autoconf_bin }}:{{ automake_bin }}:{{ matiec_location }}/bin:{{ bison_location }}/bin:{{ flex_location }}/bin:{{ bin_directory }}:{{ gcc_location }}/bin:$PATH
+  export XDG_DATA_DIR={{ gtk3_location }}/share
+  export GSETTINGS_SCHEMA_DIR={{ gtk3_location }}/share/glib-2.0/schemas
+  export FONTCONFIG_FILE=${font-config:output}
+  export DISPLAY=${xserver:display}
+  export XORG_LOCK_DIR=${xserver:lock-dir}
+  export LANG=C.UTF-8
+  export LC_ALL=C.UTF-8
+  export BEREMIZPYTHONPATH={{ python_bin }}
+
+output = ${directory:bin}/beremiz-env.sh
+
+[beremiz-x11]
+recipe = slapos.recipe.template
+inline =
+  #!/bin/sh -e
+  . ${beremiz-env.sh:output}
+  # wait a bit for xserver
+  sleep 1
+  exec {{ python_bin }} {{ beremiz_location }}/Beremiz.py
+output = ${directory:bin}/beremiz-x11
+
+[xterm]
+recipe = slapos.recipe.template
+inline =
+  #!/bin/sh -e
+  . ${beremiz-env.sh:output}
+  export SHELL={{bash_bin}}
+  exec {{ xterm_bin }}
+output = ${directory:bin}/xterm
+
+[request-vnc-frontend]
+<= slap-connection
+recipe = slapos.cookbook:requestoptional
+slave = true
+config-https-only = True
+config-type = websocket
+config-url = https://[${novnc-instance:ip}]:${novnc-instance:port}
+return = secure_access domain
+software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
+software-type = RootSoftwareInstance
+name = Beremiz VNC
+
+
+[publish-connection-information]
+<= monitor-publish
+recipe = slapos.cookbook:publish
+backend-url = https://[${novnc-instance:ip}]:${novnc-instance:port}/vnc.html?host=[${novnc-instance:ip}]&port=${novnc-instance:port}&encrypt=1
+url = ${request-vnc-frontend:connection-secure_access}/vnc.html?host=${request-vnc-frontend:connection-domain}&port=443&encrypt=1
+vnc-password = ${random-password:passwd}
+
+[buildout]
+extends =
+  {{ template_logrotate }}
+{{ '  ' ~ template_monitor }}
+
+parts =
+  monitor-base
+  fluxbox
+  novnc-promise
+  xserver-promise
+  x11vnc-listen-promise
+  beremiz-x11
+  websockify-sighandler-service
+  request-vnc-frontend
+  generate-vnc-password
+  publish-connection-information
+
+eggs-directory = {{ eggs_directory }}
+develop-eggs-directory = {{ develop_eggs_directory }}
+offline = true
diff --git a/software/beremiz-ide/instance.cfg.in b/software/beremiz-ide/instance.cfg.in
new file mode 100644
index 000000000..69c447927
--- /dev/null
+++ b/software/beremiz-ide/instance.cfg.in
@@ -0,0 +1,84 @@
+[buildout]
+parts = switch-softwaretype
+
+eggs-directory = {{ buildout_egg_directory }}
+develop-eggs-directory = {{ buildout_develop_directory }}
+
+[switch-softwaretype]
+recipe = slapos.cookbook:switch-softwaretype
+default  = template-{{ instance_template_type }}:output
+RootSoftwareInstance = ${:default}
+test = template-beremiz-test:output
+
+[slap-configuration]
+recipe = slapos.cookbook:slapconfiguration.serialised
+computer = ${slap-connection:computer-id}
+partition = ${slap-connection:partition-id}
+url = ${slap-connection:server-url}
+key = ${slap-connection:key-file}
+cert = ${slap-connection:cert-file}
+
+[jinja2-template-base]
+recipe = slapos.recipe.template:jinja2
+output = ${buildout:directory}/${:filename}
+extensions = jinja2.ext.do
+extra-context =
+context =
+  key develop_eggs_directory   buildout:develop-eggs-directory
+  key buildout_directory       buildout:directory
+  key eggs_directory           buildout:eggs-directory
+  key ipv4                     slap-configuration:ipv4
+  key ipv6                     slap-configuration:ipv6
+  key global_ipv4_prefix       network-information:global-ipv4-network
+  key slapparameter_dict       slap-configuration:configuration
+  key computer_id              slap-configuration:computer
+  raw bin_directory            {{ bin_directory }}
+  raw template_monitor         {{ template_monitor_cfg }}
+  raw template_logrotate       {{ logrotate_cfg }}
+  raw logrotate_cfg            {{ logrotate_cfg }}
+  raw python_bin               {{ python_bin }}
+  ${:extra-context}
+
+[template-beremiz]
+<= jinja2-template-base
+url = {{ template_instance_beremiz }}
+filename = instance-beremiz.cfg
+extra-context =
+  raw autoconf_bin       {{ autoconf_location }}/bin
+  raw automake_bin       {{ automake_location }}/bin
+  raw bash_bin           {{ bash_location }}/bin/bash
+  raw beremiz_location   {{ beremiz_location }}
+  raw bison_location     {{ bison_location }}
+  raw flex_location      {{ flex_location }}
+  raw fluxbox_location   {{ fluxbox_location }}
+  raw fluxbox_menu       {{ template_fluxbox_menu }}
+  raw font_config_loc    {{ fontconfig_location }}
+  raw font_config_tmplt  {{ template_fonts_conf_target }}
+  raw font_dejavu        {{ font_dejavu }}
+  raw font_liberation    {{ font_liberation }}
+  raw gcc_location       {{ gcc_location }}
+  raw git_bin_dir        {{ git_location }}
+  raw gtk3_location      {{ gtk3_location }}
+  raw matiec_location    {{ matiec_location }}
+  raw mesa_location      {{ mesa_location }}
+  raw novnc_location     {{ novnc_location }}
+  raw openssl_bin        {{ openssl_location }}/bin/openssl
+  raw websockify_bin     {{ bin_directory }}/websockify
+  raw x11vnc_bin         {{ x11vnc_location }}/bin/x11vnc
+  raw xvfb_bin           {{ xserver_location }}/bin/Xvfb
+  raw xterm_bin          {{ xterm_location }}/bin/xterm
+
+[template-beremiz-test]
+<= jinja2-template-base
+url = {{ template_instance_beremiz_test }}
+filename = instance-beremiz-test.cfg
+extra-context =
+  key instance_beremiz   template-beremiz:output
+  raw beremiz_location   {{ beremiz_location }}
+  raw sikulix_bin        {{ sikulix_bin }}
+  raw xvfb_bin           {{ xserver_location }}/bin/Xvfb
+  raw wmctrl_bin         {{ wmctrl_location }}/bin/wmctrl
+  raw instance_nxdtest   {{ nxdtest_template }}
+  raw xdotool_bin        {{ xdotool_location }}/bin/xdotool
+  raw opencv_location    {{ opencv_location }}
+  raw nxdtest_dir        {{ buildout_directory }}
diff --git a/software/beremiz-ide/software.cfg b/software/beremiz-ide/software.cfg
new file mode 100644
index 000000000..d768e71b1
--- /dev/null
+++ b/software/beremiz-ide/software.cfg
@@ -0,0 +1,205 @@
+[buildout]
+
+extends =
+  ../../stack/slapos.cfg
+  ../../component/autoconf/buildout.cfg
+  ../../component/flex/buildout.cfg
+  ../../component/fonts/buildout.cfg
+  ../../component/noVNC/buildout.cfg
+  ../../component/nginx/buildout.cfg
+  ../../component/lxml-python/buildout.cfg
+  ../../component/numpy/buildout.cfg
+  ../../component/numpy/openblas.cfg
+  ../../component/matplotlib/buildout.cfg
+  ../../component/wxpython/buildout.cfg
+  ../../component/matiec/buildout.cfg
+  ../../component/mesa/buildout.cfg
+  ../../component/libvnc/buildout.cfg
+  ../../component/open62541/buildout.cfg
+  ../../component/sikuli/buildout.cfg
+  ../../component/fluxbox/buildout.cfg
+  ../../component/pygolang/buildout.cfg
+  ../../component/xorg/buildout.cfg
+  ../../component/pytest/buildout.cfg
+  ../../component/opencv/buildout.cfg
+  ../../component/xterm/buildout.cfg
+  ../../stack/monitor/buildout.cfg
+  ../../stack/nxdtest.cfg
+  ./buildout.hash.cfg
+
+parts +=
+  slapos-cookbook
+  beremiz-eggs
+  open62541
+  Modbus
+  xterm
+  instance
+
+[gcc]
+# Always build GCC for Fortran (see openblas).
+max_version = 0
+
+[python]
+# Beremiz works with python2.7 for now, the code is not yet upgraded for python3
+part = python2.7
+
+[open62541]
+# Beremiz need it to be in folder parts/open62541
+# as Beremiz search for open62541 to BEREMIZ_PATH/../open62541
+shared = false
+post-install =
+  mkdir -p @@LOCATION@@/build/bin
+  ln -sf @@LOCATION@@/lib/libopen62541.a @@LOCATION@@/build/bin/libopen62541.a
+
+[twisted]
+recipe  = zc.recipe.egg:custom
+egg     =  twisted
+setup-eggs =
+    six
+    pathlib
+    incremental
+
+[beremiz-eggs]
+recipe = zc.recipe.egg
+eggs =
+  ${wxPython:egg}
+  ${python-cryptography:egg}
+  ${lxml-python:egg}
+  ${matplotlib:egg}
+  future
+  websockify
+  zeroconf2
+  enum34
+  pyro
+  ${twisted:egg}
+  nevow
+  autobahn
+  pycountry
+  fonttools
+  ${beremiz-setup:egg}
+  opcua
+  msgpack
+
+[python-interpreter]
+recipe = zc.recipe.egg:scripts
+interpreter = python
+eggs =
+  ${beremiz-eggs:eggs}
+  ${pygolang:egg}
+# Beremiz will use sys.executable to start some internal processes
+# then they won't have installed eggs in sys.path.
+# -> use pymain from gpython to workaround that and generate
+# python where sys.executable point to interpreter
+initialization =
+  from gpython import pymain
+  pymain(sys.argv)
+  sys.exit(0)
+
+[beremiz]
+recipe = slapos.recipe.build:download-unpacked
+# download beremiz at revision bc72db1aa49fb5deabc950ed587772d027733a26
+url = https://github.com/beremiz/beremiz/archive/bc72db1aa49fb5deabc950ed587772d027733a26.tar.gz
+md5sum = 9ae7a55d4f2e673cbc8d2c4528e069b9
+
+[beremiz-setup]
+recipe  = zc.recipe.egg:develop
+egg = beremiz
+setup = ${beremiz:location}
+
+[download-template]
+recipe = slapos.recipe.build:download
+url = ${:_profile_base_location_}/${:filename}
+
+[instance]
+recipe = slapos.recipe.template:jinja2
+output = ${buildout:directory}/instance.cfg
+url = ${:_profile_base_location_}/${:filename}
+python-bin = ${buildout:bin-directory}/${python-interpreter:interpreter}
+type = beremiz
+context =
+    key autoconf_location  autoconf:location
+    key automake_location  automake:location
+    key bash_location bash:location
+    key beremiz_location beremiz-setup:setup
+    key bin_directory buildout:bin-directory
+    key buildout_egg_directory buildout:eggs-directory
+    key buildout_develop_directory buildout:develop-eggs-directory
+    key buildout_directory buildout:directory
+    key template_fluxbox_menu template-fluxbox-menu.in:target
+    key template_fonts_conf_target template-fonts-conf:output
+    key template_monitor_cfg monitor2-template:rendered
+    key template_instance_beremiz template-instance-beremiz:target
+    key template_instance_beremiz_test template-instance-beremiz-test:target
+    key template_logrotate template-logrotate-base:rendered
+    key fontconfig_location fontconfig:location
+    key font_dejavu dejavu-fonts:location
+    key font_liberation liberation-fonts:location
+    key logrotate_cfg template-logrotate-base:rendered
+    key bison_location bison:location
+    key flex_location flex:location
+    key fluxbox_location fluxbox:location
+    key gcc_location gcc:prefix
+    key git_location git:location
+    key gtk3_location gtk-3:location
+    key instance_template_type :type
+    key matiec_location matiec:location
+    key mesa_location mesa:location
+    key novnc_location noVNC:location
+    key nxdtest_template nxdtest-instance.cfg:rendered
+    key python_bin :python-bin
+    key opencv_location opencv:location
+    key openssl_location openssl:location
+    key sikulix_bin sikuli:output
+    key xdotool_location xdotool:location
+    key xserver_location xserver:location
+    key xterm_location xterm:location
+    key x11vnc_location x11vnc:location
+    key wmctrl_location wmctrl:location
+
+[template-instance-beremiz]
+<= download-template
+output = ${buildout:directory}/instance-beremiz.cfg.jinja2
+
+[template-instance-beremiz-test]
+<= download-template
+output = ${buildout:directory}/instance-beremiz-test.cfg.jinja2
+
+[template-fluxbox-menu.in]
+<= download-template
+output = ${buildout:directory}/fluxbox-menu.in
+
+[versions]
+Pillow = 6.2.2
+matplotlib = 2.2.5
+kiwisolver = 1.1.0
+cycler = 0.10.0
+websockify = 0.9.0
+Pyro = 3.16
+zeroconf2 = 0.19.2
+cython = 0.29.24
+sphinx = 1.8.5
+doc2dash = 2.3.0
+Twisted = 20.3.0
+autobahn = 19.11.2
+attrs = 19.2.0
+Automat = 0.3.0
+zope.interface = 4.4.2
+Nevow = 0.14.5
+PyHamcrest = 2.0.3
+constantly = 15.1.0
+hyperlink = 21.0.0
+incremental = 21.3.0
+future = 0.18.2
+pycountry = 18.12.8
+fonttools = 3.44.0
+idna = 2.10
+PyHamcrest = 2.0.2
+txaio = 18.8.1
+characteristic = 14.3.0
+typing = 3.10.0.0
+ifcfg = 0.22
+opcua = 0.98.13
+futures = 3.3.0
+trollius = 2.2.1
+pathlib = 1.0.1
+ddt = 1.4.4
diff --git a/software/beremiz-ide/test.cfg b/software/beremiz-ide/test.cfg
new file mode 100644
index 000000000..c66faea76
--- /dev/null
+++ b/software/beremiz-ide/test.cfg
@@ -0,0 +1,62 @@
+[buildout]
+
+extends =
+    software.cfg
+    ../../component/git/buildout.cfg
+    ../../component/pytest/buildout.cfg
+
+[beremiz-repository]
+recipe  = slapos.recipe.build:gitclone
+repository = https://github.com/beremiz/beremiz
+branch  = wxPython4
+location = ${buildout:parts-directory}/beremiz
+git-executable = ${git:location}/bin/git
+
+[beremiz-setup]
+setup = ${beremiz-repository:location}
+depends =
+  ${beremiz-gen-nxdtest:recipe}
+
+[ddt]
+recipe  = zc.recipe.egg:custom
+egg     =  ddt
+setup-eggs =
+  enum34
+
+[python-interpreter]
+eggs +=
+  ${pytest:eggs}
+  pytest-timeout
+  ${ddt:egg}
+
+[instance]
+type = beremiz-test
+
+[gen-nxdtest.sh]
+recipe = slapos.recipe.template
+output = ${buildout:parts-directory}/gennxdtest.sh
+nxdtest = ${buildout:directory}/.nxdtest
+inline =
+  #!/bin/sh -e
+  cd ${beremiz-repository:location}/tests/ide_tests/
+  testlist=$(ls -d *.sikuli)
+  rm -f ${:nxdtest}
+  for test in $testlist; do
+    if [ -z "$test" ]; then
+      continue;
+    fi
+    # beremiztest script is generated by the instance (call make test_dir=xxx xserver_command=xxx)
+    cat <<EOF >> ${:nxdtest}
+  TestCase(
+      "$test",
+      ['beremiztest', '$test'],
+      cwd="""${beremiz-repository:location}/tests""",
+      summaryf=UnitTest.summary,
+  )
+  EOF
+  done
+
+[beremiz-gen-nxdtest]
+recipe = plone.recipe.command
+command = ${gen-nxdtest.sh:output}
+update-command = ${:command}
diff --git a/software/beremiz-ide/test/README.md b/software/beremiz-ide/test/README.md
new file mode 100644
index 000000000..333372329
--- /dev/null
+++ b/software/beremiz-ide/test/README.md
@@ -0,0 +1,2 @@
+
+Beremiz IDE tests Software Release
diff --git a/software/beremiz-ide/test/setup.py b/software/beremiz-ide/test/setup.py
new file mode 100644
index 000000000..63f2521e7
--- /dev/null
+++ b/software/beremiz-ide/test/setup.py
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# 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.beremiz_ide'
+with open("README.md") as f:
+  long_description = f.read()
+
+setup(name=name,
+      version=version,
+      description="Beremiz IDE test",
+      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',
+          'requests',
+      ],
+      zip_safe=True,
+      test_suite='test',
+      )
diff --git a/software/beremiz-ide/test/test.py b/software/beremiz-ide/test/test.py
new file mode 100644
index 000000000..07d7e4acb
--- /dev/null
+++ b/software/beremiz-ide/test/test.py
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# Copyright (c) 2019 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 __future__ import unicode_literals
+
+import os
+import requests
+
+from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass
+
+
+setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass(
+    os.path.abspath(
+        os.path.join(os.path.dirname(__file__), '../software.cfg')))
+
+class TestBeremizInstance(SlapOSInstanceTestCase):
+  __partition_reference__ = 'B'
+
+  @classmethod
+  def getInstanceSoftwareType(cls):
+    return 'default'
+
+  def setUp(self):
+    self.connection_parameters = self.computer_partition.getConnectionParameterDict()
+
+  def test_url_get(self):
+    resp = requests.get(self.connection_parameters['backend-url'], verify=False)
+    self.assertEqual(requests.codes.ok, resp.status_code)
-- 
2.30.9