Back to Repositories

Testing Processor Count and CPU Resource Management in concurrent-ruby

This test suite validates processor count and CPU resource management functionality in the concurrent-ruby library. It covers essential methods for determining processor counts and handling CPU quotas across different cgroup versions in Linux environments.

Test Coverage Overview

The test suite provides comprehensive coverage of processor counting and CPU resource management features:

  • Processor count validation for both logical and physical cores
  • CPU quota detection and calculation across cgroups v1 and v2
  • Available processor count determination based on quotas
  • CPU shares calculation for resource allocation

Implementation Analysis

The testing approach employs RSpec’s behavior-driven development framework to validate processor counting functionality. It uses extensive mocking to simulate different cgroup configurations and file system responses, particularly for Linux-specific features.

Key patterns include:
  • Context-specific test cases for different cgroup versions
  • Expectation chaining for complex scenarios
  • File system interaction mocking

Technical Details

Testing tools and configuration:
  • RSpec for test structure and assertions
  • RbConfig for OS-specific testing
  • File system mocking for cgroup interactions
  • ProcessorCounter utility class for core functionality

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Comprehensive edge case coverage for different OS environments
  • Isolated test contexts with proper setup
  • Clear test descriptions that document functionality
  • Effective use of let blocks for shared resources
  • Thorough validation of return types and values

ruby-concurrency/concurrent-ruby

spec/concurrent/utility/processor_count_spec.rb

            
require 'concurrent/utility/processor_counter'

module Concurrent

  RSpec.describe '#processor_count' do

    it 'returns a positive integer' do
      expect(Concurrent::processor_count).to be_a Integer
      expect(Concurrent::processor_count).to be >= 1
    end
  end

  RSpec.describe '#physical_processor_count' do

    it 'returns a positive integer' do
      expect(Concurrent::physical_processor_count).to be_a Integer
      expect(Concurrent::physical_processor_count).to be >= 1
    end
  end

  RSpec.describe '#cpu_quota' do

    let(:counter) { Concurrent::Utility::ProcessorCounter.new }

    it 'returns #compute_cpu_quota' do
      expect(Concurrent::cpu_quota).to be == counter.cpu_quota
    end

    it 'returns nil if no quota is detected' do
      if RbConfig::CONFIG["target_os"].include?("linux")
        expect(File).to receive(:exist?).twice.and_return(nil) # Checks for cgroups V1 and V2
      end
      expect(counter.cpu_quota).to be_nil
    end

    it 'returns nil if cgroups v2 sets no limit' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(true)
      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.max").and_return("max 100000\n")
      expect(counter.cpu_quota).to be_nil
    end

    it 'returns a float if cgroups v2 sets a limit' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(true)
      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.max").and_return("150000 100000\n")
      expect(counter.cpu_quota).to be == 1.5
    end

    it 'returns nil if cgroups v1 sets no limit' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(false)
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return(true)

      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return("max\n")
      expect(counter.cpu_quota).to be_nil
    end

    it 'returns nil if cgroups v1 and cpu.cfs_quota_us is -1' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(false)
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return(true)

      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return("-1\n")
      expect(counter.cpu_quota).to be_nil
    end

    it 'returns a float if cgroups v1 sets a limit' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(false)
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return(true)

      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return("150000\n")
      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").and_return("100000\n")
      expect(counter.cpu_quota).to be == 1.5
    end

  end

  RSpec.describe '#available_processor_count' do

    it 'returns #processor_count if #cpu_quota is nil' do
      expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(nil)
      available_processor_count = Concurrent.available_processor_count
      expect(available_processor_count).to be == Concurrent::processor_count
      expect(available_processor_count).to be_a Float
    end

    it 'returns #processor_count if #cpu_quota is higher' do
      expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f * 2)
      available_processor_count = Concurrent.available_processor_count
      expect(available_processor_count).to be == Concurrent::processor_count
      expect(available_processor_count).to be_a Float
    end

    it 'returns #cpu_quota if #cpu_quota is lower than #processor_count' do
      expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f / 2)
      available_processor_count = Concurrent.available_processor_count
      expect(available_processor_count).to be == Concurrent::processor_count.to_f / 2
      expect(available_processor_count).to be_a Float
    end

  end

  RSpec.describe '#cpu_shares' do
    let(:counter) { Concurrent::Utility::ProcessorCounter.new }

    it 'returns a float when cgroups v2 sets a cpu.weight' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.weight").and_return(true)

      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.weight").and_return("10000\n")
      expect(counter.cpu_shares).to be == 256.0
    end

    it 'returns a float if cgroups v1 sets a cpu.shares' do
      expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.weight").and_return(false)
      expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu/cpu.shares").and_return(true)

      expect(File).to receive(:read).with("/sys/fs/cgroup/cpu/cpu.shares").and_return("512\n")
      expect(counter.cpu_shares).to be == 0.5
    end

  end
end