Smartphone Test Farm (STF)

repository·master·Indexed 26 days ago

https://github.com/devicefarmer/stf

A smartphone test farm for managing and interacting with mobile devices. Features include a RESTful API for device management, remote ADB connectivity, and a web UI. Documentation covers API authentication via OAuth 2.0, Node.js and shell integration using swagger-client and curl, Docker image builds for armv7l, and UI components like nine-bootstrap and stf-tooltips.

Tokens
43.7K
Snippets
61
Records
398
Agent score
88%

What's inside stf

  1. Deployment Architecture Overview

    master

    STF (Smartphone Test Lab) is deployed as a collection of independent processes called "units" that communicate via ZeroMQ and Protocol Buffers. A typical deployment uses systemd and Docker to manage these units across multiple hosts. The architecture is divided into several functional roles:

    • Provider role: Manages physical device connections (requires adbd.service and stf-provider@.service).
    • App role: Contains the core logic and services (e.g., stf-app@.service, stf-api@.service, stf-websocket@.service).
    • Database role: Manages data persistence via RethinkDB.
    • Proxy role: Uses a reverse proxy (like Nginx) to unify HTTP-based units.
  2. Understand VNC authentication behavior in STF

    master

    STF uses a modified version of the standard VNC authentication protocol to support multi-user environments without usernames.

    Key characteristics:

    • Static Challenge: Instead of a random challenge, STF sends a static challenge (e.g., 16 zeroes) to the client.
    • User Identification: The server identifies the user based on the challenge response returned by the client. Because the challenge is static, the response is unique and constant for each password, allowing the server to map the response to a specific user/password without needing a username.
    • Per-Device Passwords: Each password is valid for only a single device. This design is intended to facilitate future proxying and load balancing, potentially allowing multiple devices to be exposed via a single port.
    • Security Warning: This implementation is susceptible to eavesdropping because responses from previous sessions can be reused. It is highly recommended that all VNC connections run inside a secure tunnel or within a trusted internal network.
  3. Authenticate with the STF API

    master

    STF uses OAuth 2.0 for authentication. To use the API, you must generate an access token via the STF UI:

    1. Navigate to the Settings tab.
    2. Generate a new token in the Keys section.
    3. Save the token immediately, as it cannot be retrieved again.

    The access token must be included in the Authorization header of every request as a Bearer token.

    curl -H "Authorization: Bearer YOUR-TOKEN-HERE" https://stf.example.org/api/v1/user
  4. Configure the STF App unit

    master

    The stf-app@.service unit provides the main HTTP server and serves all static resources (images, scripts, stylesheets). It is a template unit where the instance identifier specifies the exposed port (e.g., stf-app@3100.service runs on port 3100). It requires rethinkdb-proxy-28015.service on the same host. You must configure the --auth-url to match your chosen authentication method.

    [Unit]
    Description=STF app
    After=rethinkdb-proxy-28015.service
    BindsTo=rethinkdb-proxy-28015.service
    
    [Service]
    EnvironmentFile=/etc/environment
    TimeoutStartSec=0
    Restart=always
    ExecStartPre=/usr/bin/docker pull devicefarmer/stf:latest
    ExecStartPre=-/usr/bin/docker kill %p-%i
    ExecStartPre=-/usr/bin/docker rm %p-%i
    ExecStart=/usr/bin/docker run --rm \
      --name %p-%i \
      --link rethinkdb-proxy-28015:rethinkdb \
      -e "SECRET=YOUR_SESSION_SECRET_HERE" \
      -p %i:3000 \
      devicefarmer/stf:latest \
      stf app --port 3000 \
        --auth-url https://stf.example.org/auth/mock/ \
        --websocket-url wss://stf.example.org/
    ExecStop=-/usr/bin/docker stop -t 10 %p-%i
  5. Configure the STF Provider unit

    master

    The stf-provider@.service connects to ADB and manages worker processes for each device. It requires adbd.service on the same host.

    Constraints:

    • Only one provider unit can run per host to avoid device control competition.
    • The instance identifier is used as a provider ID for matching in Nginx.
    • If using self-signed certs, add -e "NODE_TLS_REJECT_UNAUTHORIZED=0" to the docker run command.
    [Service]
    ...
    ExecStart=/usr/bin/docker run --rm \
      --name %p-%i \
      --link adbd:adbd \
      -p 15000-25000:15000-25000 \
      devicefarmer/stf:latest \
      stf provider \
        --name "%H/%i" \
        --connect-sub tcp://devside.stf.example.org:7250 \
        --connect-push tcp://devside.stf.example.org:7270 \
        --storage-url https://stf.example.org/ \
        --public-ip ${COREOS_PRIVATE_IPV4} \
        --min-port=15000 \
        --max-port=25000 \
        --heartbeat-interval 10000 \
        --screen-ws-url-pattern "wss://stf.example.org/d/%i/<%= serial %>/<%= publicPort %>/" \
        --adb-host adbd
  6. Use nice-tabs with current syntax

    master

    The nice-tabs component provides a tabbed interface with support for Font Awesome icons, template preloading, and state persistence (saving the last selected tab to localForage).

    In the current syntax, you must pass a tabs array to the component via a scope variable. Each object in the array should contain:

    • title: The text label for the tab.
    • icon: A Font Awesome icon class (e.g., fa-bolt).
    • templateUrl: The path to the template to be loaded for that tab.

    You can also specify a key (for persistence) and a direction (e.g., below).

  7. Use stf-tooltips in Angular templates

    master

    The stf-tooltips component (based on Angular Bootstrap) allows you to define tooltips using two simple attributes: help-title and help-key. The component automatically maps these to a formatted HTML tooltip.

    To use it, provide a title (which can be localized using the translate pipe) and a key (representing the specific action or keypress).

    help-title='{{"Run Command"|translate}}'
    help-key='Enter'
  8. Store device log events in RethinkDB

    master

    You can use the stf-log-rethinkdb.service unit to store device log events into RethinkDB. This unit requires the rethinkdb-proxy-28015.service unit to be running on the same host. Note that running more than one instance of this unit is not recommended as it will result in duplicate event recording.

    [Unit]
    Description=STF RethinkDB log recorder
    After=rethinkdb-proxy-28015.service
    BindsTo=rethinkdb-proxy-28015.service
    
    [Service]
    EnvironmentFile=/etc/environment
    TimeoutStartSec=0
    Restart=always
    ExecStartPre=/usr/bin/docker pull devicefarmer/stf:latest
    ExecStartPre=-/usr/bin/docker kill %p
    ExecStartPre=-/usr/bin/docker rm %p
    ExecStart=/usr/bin/docker run --rm \
      --name %p \
      --link rethinkdb-proxy-28015:rethinkdb \
      devicefarmer/stf:latest \
      stf log-rethinkdb \
        --connect-sub tcp://appside.stf.example.org:7150
    ExecStop=-/usr/bin/docker stop -t 10 %p
  9. Deploy Swagger UI for STF API testing

    master

    The swagger-ui@.service template unit allows you to interact with the STF API via a web-based Swagger UI. This unit requires the main STF HTTP server to be running on the same host.

    To use this, place the STF swagger file api_v1.yaml in the /opt/stf/swagger directory on the host. You can run multiple instances on the same host by using different ports via the template identifier.

    [Unit]
    Description=Swagger UI (runs on %i port)
    After=docker.service
    BindsTo=docker.service
    
    [Service]
    EnvironmentFile=/etc/environment
    TimeoutStartSec=0
    Restart=always
    ExecStartPre=/usr/bin/docker pull swaggerapi/swagger-ui:latest
    ExecStartPre=-/usr/bin/docker kill %p-%i
    ExecStartPre=-/usr/bin/docker rm %p-%i
    ExecStart=/usr/bin/docker run --rm \
      --name %p-%i \
      -e "VALIDATOR_URL=null" \
      -e "SWAGGER_JSON=/foo/api_v1.yaml" \
      -p %i:8080 \
      -v /opt/stf/swagger:/foo \
      swaggerapi/swagger-ui:latest
    ExecStop=/usr/bin/docker stop -t 2 %p-%i
  10. Configure STF WebSocket unit

    master

    The stf-websocket@.service provides the communication layer between client-side JavaScript and the server-side ZeroMQ+Protobuf combination. It requires rethinkdb-proxy-28015.service on the same host. It is a template unit.

    [Service]
    ...
    ExecStart=/usr/bin/docker run --rm \
      --name %p-%i \
      --link rethinkdb-proxy-28015:rethinkdb \
      -e "SECRET=YOUR_SESSION_SECRET_HERE" \
      -p %i:3000 \
      devicefarmer/stf:latest \
      stf websocket --port 3000 \
        --storage-url https://stf.example.org/ \
        --connect-sub tcp://appside.stf.example.org:7150 \
        --connect-push tcp://appside.stf.example.org:7170
  11. Run STF Database Migrations

    master

    The stf-migrate.service is a oneshot unit used to migrate the database to the latest version (creating tables and indexes). It requires rethinkdb-proxy-28015.service on the same host. It shuts down automatically after completion.

    [Service]
    EnvironmentFile=/etc/environment
    Type=oneshot
    ExecStartPre=/usr/bin/docker pull devicefarmer/stf:latest
    ExecStartPre=-/usr/bin/docker kill %p
    ExecStartPre=-/usr/bin/docker rm %p
    ExecStart=/usr/bin/docker run --rm \
      --name %p \
      --link rethinkdb-proxy-28015:rethinkdb \
      -e "STF_ROOT_GROUP_NAME=YOUR_ROOT_GROUP_NAME_HERE" \
      -e "STF_ADMIN_NAME=YOUR_ADMIN_NAME_HERE" \
      -e "STF_ADMIN_EMAIL=YOUR_ADMIN_EMAIL_HERE" \
      devicefarmer/stf:latest \
      stf migrate