LogoLogo
  • PlaceOS Documentation
  • Overview
    • Key Concepts
      • Drivers
      • Interfaces
      • Modules
      • Settings
      • Systems
      • Triggers
      • Zones
    • Languages
      • Crystal
      • TypeScript
    • Protocols
      • MQTT
      • SAML
      • OAuth2
  • How To
    • Configure PlaceOS for Microsoft 365
      • Step 1: Room Calendar Access
        • Create Azure App Registration (Application Permissions)
        • Exchange Calendar Group
        • Limit Application Permissions
        • Configure PlaceOS Calendar Driver
      • Step 2: User Authentication & Calendar Access
        • Create a PlaceOS Authentication Source
        • Create Azure App Registration (Delegated Permissions)
        • Configure PlaceOS Authentication Source
        • Add User Login Redirects
      • Concierge Access
      • Troubleshooting
        • Blocked or Blacklisted IP Error
    • Configure PlaceOS for Google Workspace
      • Google Configuration
        • Create Google Cloud Project & Enable API
        • Configure Google Cloud Service Account
        • Add Google Workplace Permissions
        • Create Google Marketplace App (optional)
        • Google Workspace Service User (RBAC)
        • Configure Access to Google Resource Calendars
      • User Authentication
        • Create a PlaceOS Authentication Source for Google
        • Create Google Cloud OAuth2 Client App
        • Configure PlaceOS Auth Source for Google
        • Add User Login Redirects
    • Deployment
      • Deploy AWS Fargate on Modular CloudFormation Stacks
      • Deploy AWS Fargate on Nested CloudFormation Stacks
      • Writing Import Scripts
    • Analytics
      • MQTT Integration
    • Backoffice
      • Add a Domain to PlaceOS
      • Backoffice File Upload
      • Configure Staff API
      • Calendar Driver
      • Enable Sensor UI
      • Bookings Driver
      • Configure a webhook
    • Authentication
      • Azure B2C
        • Azure B2C Custom Policy Framework
        • Configure PlaceOS for Azure B2C
        • 365 Room Resources on Azure B2C
      • Configure SAML SSO
        • Configure SAML2 with AD FS
        • Configure SAML2 with Auth0
        • Configure SAML2 with Azure AD
        • Configure SAML2 with Google Workspace
      • Configure OAuth2 SSO
      • X-API Keys
      • Bearer tokens
    • Location Services
      • Location Services
      • Area Management
      • Discovering User Devices
      • Locating Users on a Network
      • People Finding with Cisco Meraki on PlaceOS
      • People Finding with Juniper Mist on PlaceOS
    • Notifications
      • Catering Orders
    • User Interfaces
      • Booking Panel App
      • Workplace App
      • Native Booking Panel App
      • Deploy a Frontend Interface
      • Microsoft Outlook Plugin
      • Configure Endpoint Auto Login
      • SVG Map Creation
      • Configuring a default UI
  • Tutorials
    • Setup a dev environment
    • Backend
      • Troubleshooting Backend Failures
      • Import Bookable Rooms
      • Writing A Driver
        • Testing drivers
        • ChatGPT / LLM Capabilities
          • Native GPT Plugins
      • Testing Internal Builds
    • Backoffice
      • Adding Drivers & Modules
      • Add Zone Structure
    • Common Configurations
      • Asset Manager
      • Catering
      • Locker Booking
      • Webex Instant Connect
      • Desk booking
      • Sensor Data Collection
        • Configure Kontakt IO
        • Configuring Meraki
        • Configuring DNA Spaces
      • Elevated Privileges
  • Reference
    • API
      • Real-time Websocket
      • Rest API
      • Staff API
    • Drivers
      • PlaceOS
        • Bookings
        • Staff API
        • Visitor Mailer
        • Lockers
      • Microsoft
        • Graph API
    • PlaceOS Skills
    • Privacy Policy
    • Recommended Products
    • Supported Integrations
    • System Architecture
    • System Functionality & Requirements
    • Infrastructure Requirements
    • Security Compliance
      • FAQ
      • GDPR
      • Security
    • Microsoft Azure Permissions
  • Glossary
  • 🎯PlaceOS Roadmap
  • 🆘PlaceOS Support
  • 👩‍💻PlaceOS Github
  • 📝PlaceOS Changelog
Powered by GitBook
On this page
  • Code documentation
  • Expectations
  • Status
  • Testing Streaming IO
  • Testing HTTP requests
  • Executing functions
  • Testing Logic
Export as PDF
  1. Tutorials
  2. Backend
  3. Writing A Driver

Testing drivers

PreviousWriting A DriverNextChatGPT / LLM Capabilities

Last updated 1 year ago

There are three kind of drivers

  • Streaming IO (TCP, SSH, UDP, Multicast, ect)

  • HTTP Client

  • Logic

From a driver code structure standpoint there is no difference between these types.

  • The same driver can be used over a TCP, UDP or SSH transport.

  • All drivers support HTTP methods if a URI endpoint is defined.

  • If a driver is associated with a System then it has access to logic helpers

During a test, the loaded module is loaded with a TCP transport, HTTP enabled and logic module capabilities. This allows for testing the full capabilities of any driver.

The driver is launched as it would be in production.

Code documentation

For detailed automatically generated documentation please see the:

Expectations

Specs have access to Crystal lang . This allows you to confirm expectations.

variable = 34
variable.should eq(34)

Status

Expectations are primarily there to test the state of the module.

  • You can access state via the status helper: status[:state_name]

  • Then you can check it an expected value: status[:state_name].should eq(14)

Testing Streaming IO

The following functions are available for testing streaming IO:

  • transmit(data) -> transmits the object to the module over the streaming IO interface

  • responds(data) -> alias for transmit

  • should_send(data, timeout = 500.milliseconds) -> expects the module to respond with the data provided

  • expect_send(timeout = 500.milliseconds) -> returns the next Bytes sent by the module (useful if the data sent is not deterministic, i.e. has a time stamp)

A common test case is to ensure that module state updates as expected after transmitting some data to it:

# transmit some data
transmit(">V:2,C:11,G:2001,B:1,S:1,F:100#")

# check that the state updated as expected
status[:area2001].should eq(1)

Testing HTTP requests

The test suite emulates a HTTP server so you can inspect HTTP requests and send canned responses to the module.

expect_http_request do |request, response|
  io = request.body
  if io
    data = io.gets_to_end
    request = JSON.parse(data)
    if request["message"] == "hello steve"
      response.status_code = 202
    else
      response.status_code = 401
    end
  else
    raise "expected request to include dialing details #{request.inspect}"
  end
end

# check that the state updated as expected
status[:area2001].should eq(1)

Use expect_http_request to access an expected request coming from the module.

  • when the block completes, the response is sent to the module

  • you can see request object details here: https://crystal-lang.org/api/latest/HTTP/Request.html

  • you can see response object details here: https://crystal-lang.org/api/latest/HTTP/Server/Response.html

Executing functions

This allows you to request actions be performed in the module via the standard public interface.

  • exec(:function_name, argument_name: argument_value) -> response a response future (async return value)

  • You should send and responds(data) before inspecting the response.get

# Execute a command
response = exec(:scene?, area: 1)

# Check that the command causes the module to send some data
should_send("?AREA,1,6\r\n")
# Respond to that command
responds("~AREA,1,6,2\r\n")

# Check if the functions return value is expected
response.get.should eq(2)
# Check if the module state is correct
status[:area1].should eq(2)

Testing Logic

Logic modules typically expect a system to contain some drivers which the logic modules interacts with.

# define mock versions of the drivers it will interact with

class Display < DriverSpecs::MockDriver
  include Interface::Powerable
  include Interface::Muteable

  enum Inputs
    HDMI
    HDMI2
    VGA
    VGA2
    Miracast
    DVI
    DisplayPort
    HDBaseT
    Composite
  end

  include PlaceOS::Driver::Interface::InputSelection(Inputs)

  # Configure initial state in on_load
  def on_load
    self[:power] = false
    self[:input] = Inputs::HDMI
  end

  # implement the abstract methods required by the interfaces
  def power(state : Bool)
    self[:power] = state
  end

  def switch_to(input : Inputs)
    mute(false)
    self[:input] = input
  end

  def mute(
    state : Bool = true,
    index : Int32 | String = 0,
    layer : MuteLayer = MuteLayer::AudioVideo
  )
    self[:mute] = state
    self[:mute0] = state
  end
end

Then you can define the system configuration, you can also change the system configuration throughout your spec to test different configurations.

DriverSpecs.mock_driver "Place::LogicExample" do

  # Where `{Display, Display}` is referencing the `MockDriver` class defined above
  # and `Display:` is the friendly name
  # so this system would have `Display_1`, `Display_2`, `Switcher_1`
  system({
    Display:  {Display, Display},
    Switcher: {Switcher},
  })

  # ...
end

Along with the physical system configuration you can test different setting configurations. Settings can also be changed throughout the life cycle of your spec.

DriverSpecs.mock_driver "Place::LogicExample" do

  settings({
    name: "Meeting Room 1",
    map_id: "1.03"
  })

end

An action you perform on your driver might be expected to update state in the mock devices. You can access this state via the system helper

DriverSpecs.mock_driver "Place::LogicExample" do

  # execute a function in your logic module
  exec(:power, true)

  # Check that the expected state has updated in you mock device
  system(:Display_1)[:power].should eq(true)

  # manually execute a function on a mock device
  # need to cast the mock to the appropriate class
  system(:Display_1).as(Display).power false
end

All status queried in this manner is returned as a JSON::Any object

Publishing events

Emulating notifications is also possible

DriverSpecs.mock_driver "Place::LogicExample" do
  publish("channel/path", {payload: "data"}.to_json)
end

There is a good overview on on the crystal lang docs site

Driver Spec API
spec expectations
how to use expectations