Simple Emscripten auto test with ChromeDriver

In this article, I will describe the implementation of running autotest for ChromeDriver of the Chrome browser, which runs the module autotest translated from C ++ using Emscripten, reads the console output and returns the result of the check.
First you need to install selenium, for python3-ubuntu this is done like this:


pip3 install selenium

Next, download ChromeDriver from the official site, put the chromedriver in / usr / local / bin for example, after which you can start implementing the autotest.
Below I will give the autotest code that launches the Chrome browser with the autotest page open on Emscripten, checks for the presence of the text “Window test succeded”:


import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

capabilities = DesiredCapabilities.CHROME
capabilities['goog:loggingPrefs'] = { 'browser':'ALL' }
driver = webdriver.Chrome()
driver.get("http://localhost/windowInitializeTest/indexFullscreen.html")

time.sleep(2)

exitCode = 1

for entry in driver.get_log('browser'):
    if entry["source"] == "console-api":
        message = entry["message"]
        if "Window test succeded" in message:
            print("Test succeded")
            exitCode = 0

driver.close()
exit(exitCode)

Save the test as main.py and run python3 main.py

Building project with libraries Emscripten

In this article, I will describe the assembly of a project consisting of several libraries using Emscripten.
Emscripten does not currently support building shared libraries, so the first thing we do is translate all the libraries from Shared to Static. Emscripten works with its include files, so you need to solve the issue with the visibility of header files, I solved it by forwarding the symlink from the system directory to the Emscripten toolchain:


ln -s /usr/local/include/FlameSteelFramework $EMSDK/fastcomp/emscripten/system/include/FlameSteelFramework

If you use CMake, then you need to change SHARED-> STATIC in the CMakeLists.txt file of the add_library method. To build a library / application for further static linking, use the commands:


emcmake cmake .
emmake make

Next, you will need to build the main application indicating * .a library files at the linking stage. I could not indicate the relative path, the assembly completed correctly only after specifying the full paths in the CMakeLists.txt file:


elseif(EMSCRIPTEN)
target_link_libraries(${FSEGT_PROJECT_NAME} GL GLEW 
/home/demensdeum/Sources/cube-art-project-bootstrap/cube-art-project/sharedLib/libCubeArtProject.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelEngineGameToolkitFSGL/libFlameSteelEngineGameToolkitFSGL.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelEngineGameToolkit/libFlameSteelEngineGameToolkit.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelCore/libFlameSteelCore.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelBattleHorn/libFlameSteelBattleHorn.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FSGL/libFSGL.a 
/home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelCommonTraits/libFlameSteelCommonTraits.a)
else()

References

https://emscripten.org/docs/compiling/Building-Projects.html#using-libraries

Shared Library CMake C++

Recently I decided to make all parts of FlameSteelFramework separate shared libraries, then I will show an example of a CMakeLists.txt file for FlameSteelCore:


cmake_minimum_required(VERSION 3.5)

project (FlameSteelCore)
set(CMAKE_BUILD_TYPE Release)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)

file(GLOB_RECURSE SOURCE_FILES
    "src/FlameSteelCore/*.cpp"
)

add_library(FlameSteelCore SHARED ${SOURCE_FILES})

install(DIRECTORY "${CMAKE_SOURCE_DIR}/src/FlameSteelCore"
        DESTINATION include/FlameSteelFramework
        FILES_MATCHING
        PATTERN "*.h"
)

install(TARGETS FlameSteelCore DESTINATION lib)

The commands that CMake executes are: it collects all files with the * .cpp extension from the src / FlameSteelCore / directory in the shared library, copies all readers with the * .h extension from src / FlameSteelCore to include / FlameSteelFramework (in my case it is / usr / local / include / FlameSteelFramework), copy shared lib to the lib directory (/ usr / local / lib)
After installation, it may be necessary to update the LD cache – sudo ldconfig.
To build and install on Ubuntu (if you have the correct build toolchain), just run the following commands:


cmake . && make && sudo make install

To check the installation process, I pass make prefix to the local makeInstallTestPlayground folder:


cmake -DCMAKE_INSTALL_PREFIX:PATH=/home/demensdeum/makeInstallTestPlayground . && make && make install

References

https://stackoverflow.com/questions/17511496/how-to-create-a-shared-library-with-cmake
https://stackoverflow.com/questions/6003374/what-is-cmake-equivalent-of-configure-prefix-dir-make-all-install

Cling – C++ interpreter

Not so long ago I came across an interesting project called Cling , a C ++ language interpreter that can work interactively from the console as well. You can check project here: https://github.com/root-project/cling
The installation for Ubuntu is the simplest – download the archive for the version you need, unzip it, go to the bin folder and run cling in the terminal.
The following is an example of loading the FlameSteelCore library, initializing the object, listing id:

Webassembly lost exceptions and regex troubles

Lost exceptions

An interesting feature of Emscripten, when starting a game loop through emscripten_set_main_loop, remember that exception handling must be re-added via try catch directly in the loop method, as runtime loses the try catch block from the outside.
The easiest way is to display the error text using the browser using javascript alert:


            catch (const std::exception &exc)
            {
                const char *errorText = exc.what();
                cout << "Exception: " << errorText << "; Stop execution" << endl;

                EM_ASM_(
                {
                    var errorText = UTF8ToString($0);
                    alert(errorText);

                }, errorText);

                abort();

Too complicated regexp

The regex implementation in std may throw an error_complexity exception if it finds the regex too complex. This happens in the current emscripten implementation, so I suggest you implement tests for parsing through regular intervals, or use third-party regex implementations.

Builder Pattern

The Builder pattern belongs to a group of patterns whose existence is not particularly clear to me, I note a clear redundancy of this. Refers to a group of generative design patterns. Used to implement a simple interface for creating complex objects.

Usage

Simplification of the interface. It can facilitate the creation of an object in constructors with a large number of arguments, objectively improve the readability of the code.

C ++ example without a builder:


auto monster = new Monster();
auto weapon = new Weapon(“Claws”);
monster->weapon = weapon;
auto health = new MonsterHealth(100);
monster->health = health;

C ++ example with a builder:


auto monster = new MonsterBuilder()
              .addWeapon(“Claws”)
              .addHealth(100)
              .build();

However, in languages ​​that support named arguments, there is no need to use it for this particular case.

Swift example using named arguments:


let monster = Monster(weapon: “Claws”, health: 100)

Immutability. Using the builder, you can ensure the encapsulation of the created object, until the final stage of assembly. Here you need to think carefully about whether the use of the pattern will save you from the high dynamics of the environment in which you work, perhaps the use of the pattern will not work, due to the simple lack of culture of using encapsulation by the development team.

Interaction with components at different stages of creating an object. Also, using the pattern, it is possible to provide step-by-step creation of an object when interacting with other components of the system. Most likely it is very useful (?)

Critics

Of course you need to * thoroughly * think about whether it is necessary to establish the widespread use of the pattern in your project. Languages ​​with modern syntax and an advanced IDE eliminate the need to use the Builder in terms of improving the readability of the code (see paragraph on named arguments)
Was it necessary to use this pattern in 1994, at the time of the release of the GoF book? Most likely yes, however, judging by the Open source code base of those years, few people used it.

References

https://refactoring.guru/design-patterns/builder

Composite Pattern

The Composite pattern refers to structural design patterns.
Suppose we are developing an application – a photo album. The user can create folders, add photos there, and perform other manipulations. You definitely need the ability to show the number of files in folders, the total number of all files and folders.
Obviously, you need to use a tree, but how to implement the architecture of the tree, with a simple and convenient interface? The Composite pattern comes to the rescue.

Sheila in Moonducks

Next, in the Directory, we implement the dataCount () method – by passing through all the elements lying in the component array, adding all their dataCount’s.
Done!
Go example:

package main

import "fmt"

type component interface {
    
    dataCount() int
    
}

type file struct {
    
}

type directory struct {

    c []component

}

func (f file) dataCount() int {

    return 1

}

func (d directory) dataCount() int {
    
    var outputDataCount int = 0
    
    for _, v := range d.c {
        outputDataCount += v.dataCount()
    }
    
    return outputDataCount
    
}

func (d *directory) addComponent(c component) {

    d.c = append(d.c, c)

}

func main() {
    
    var f file
    var rd directory
    rd.addComponent(f)
    rd.addComponent(f)
    rd.addComponent(f)
    rd.addComponent(f)

    fmt.Println(rd.dataCount())
    
    var sd directory
    sd.addComponent(f)
 
    rd.addComponent(sd)
    rd.addComponent(sd)
    rd.addComponent(sd)
    
    fmt.Println(sd.dataCount())
    fmt.Println(rd.dataCount())

}

References

https://refactoring.guru/design-patterns/composite

Adapter Pattern

Benjamín Núñez González

An adapter pattern refers to structural design patterns.

The adapter provides data / interface conversion between two classes / interfaces.

Suppose we are developing a system for determining the goal of a buyer in a store based on neural networks. The system receives a video stream from the store’s camera, determines customers according to their behavior, classifies them into groups. Types of groups – came to buy (potential buyer), just look (onlooker), came to steal something (thief), came to hand over goods (disgruntled buyer), came drunk / high (potential rowdy).

Like all experienced developers, we find a ready-made neural network that can classify species of monkeys in a cage according to the video stream, which the zoological institute of the Berlin zoo has kindly laid out for free access, retrain it on the video stream from the store and get a working state-of-the-art system.

There is only a small problem – the video stream is encoded in mpeg2 format, and our system only supports OGG Theora. We do not have the source code of the system, the only thing we can do is change the dataset and train the neural network. What to do? Write an adapter class that will translate the stream from mpeg2 -> OGG Theora and give into the neural network.

According to the classical scheme, client, target, adaptee and adapter are involved in the pattern. In this case, the client is a neural network that receives a video stream in Theora OGG, the target is the interface with which it interacts, adaptee is the interface that sends the video stream to mpeg2, the adapter converts mpeg2 to Theora OGG and sends via the target interface.

Sounds easy, right?

References

https://en.wikipedia.org/wiki/Adapter_pattern
https://refactoring.guru/design-patterns/adapter