Back to Repositories

Testing Process Execution and Environment Handling in ddollar/foreman

This test suite validates the core functionality of the Foreman::Process class, focusing on process execution and environment variable handling. It ensures reliable process management and command execution with proper environment variable expansion and UTF-8 output handling.

Test Coverage Overview

The test suite provides comprehensive coverage of process execution and environment variable handling.

Key functionality tested includes:
  • Basic process execution and output capture
  • Environment variable setting and expansion
  • UTF-8 output handling
  • Command expansion with environment variables
  • Direct process execution via Kernel.exec

Implementation Analysis

The testing approach uses RSpec’s behavior-driven development patterns with isolated unit tests. The implementation leverages Ruby’s IO.pipe for process output capture and mock expectations for exec verification.

Technical patterns include:
  • Helper method for standardized process execution
  • Binary pipe handling for cross-version compatibility
  • Mock expectations for system calls
  • Environment variable injection patterns

Technical Details

Testing tools and configuration:
  • RSpec as the testing framework
  • spec_helper for test setup
  • IO.pipe for process output capture
  • Temporary directory handling
  • Mock objects for system call verification
  • Binary encoding support for UTF-8 testing

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices with clear separation of concerns and thorough edge case coverage.

Notable practices include:
  • Isolated test cases with clear purpose
  • Comprehensive environment variable testing
  • Cross-version compatibility handling
  • proper resource cleanup
  • Explicit encoding handling

ddollar/foreman

spec/foreman/process_spec.rb

            
require 'spec_helper'
require 'foreman/process'
require 'ostruct'
require 'timeout'
require 'tmpdir'

describe Foreman::Process do

  def run(process, options={})
    rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
    process.run(options.merge(:output => wr))
    rd.gets
  end

  describe "#run" do

    it "runs the process" do
      process = Foreman::Process.new(resource_path("bin/test"))
      expect(run(process)).to eq("testing\n")
    end

    it "can set environment" do
      process = Foreman::Process.new(resource_path("bin/env FOO"), :env => { "FOO" => "bar" })
      expect(run(process)).to eq("bar\n")
    end

    it "can set per-run environment" do
      process = Foreman::Process.new(resource_path("bin/env FOO"))
      expect(run(process, :env => { "FOO" => "bar "})).to eq("bar\n")
    end

    it "can handle env vars in the command" do
      process = Foreman::Process.new(resource_path("bin/echo $FOO"), :env => { "FOO" => "bar" })
      expect(run(process)).to eq("bar\n")
    end

    it "can handle per-run env vars in the command" do
      process = Foreman::Process.new(resource_path("bin/echo $FOO"))
      expect(run(process, :env => { "FOO" => "bar" })).to eq("bar\n")
    end

    it "should output utf8 properly" do
      process = Foreman::Process.new(resource_path("bin/utf8"))
      expect(run(process)).to eq(Foreman.ruby_18? ? "\xFF\x03\n" : "\xFF\x03\n".force_encoding('binary'))
    end

    it "can expand env in the command" do
      process = Foreman::Process.new("command $FOO $BAR", :env => { "FOO" => "bar" })
      expect(process.expanded_command).to eq("command bar $BAR")
    end

    it "can expand extra env in the command" do
      process = Foreman::Process.new("command $FOO $BAR", :env => { "FOO" => "bar" })
      expect(process.expanded_command("BAR" => "qux")).to eq("command bar qux")
    end

    it "can execute" do
      expect(Kernel).to receive(:exec).with("bin/command")
      process = Foreman::Process.new("bin/command")
      process.exec
    end

    it "can execute with env" do
      expect(Kernel).to receive(:exec).with("bin/command bar")
      process = Foreman::Process.new("bin/command $FOO")
      process.exec(:env => { "FOO" => "bar" })
    end

  end

end