Search code examples
rubybundlerminitest

Minitest sometimes terminating prematurely with netither completion nor error message


I have a test file, ipca_test.rb:

require "test_helper"
require "matrix" # Does needing to include this here mean I'm doing something wrong?

class IpcaTest < Minitest::Test
  def test_that_it_has_a_version_number
    refute_nil ::Ipca::VERSION
  end

  def test_it_does_something_useful
    refute false
  end

  def test_on_a_random_matrix
    p = rand(3..10)
    n = rand(20..50)
    m = Matrix.build(n, p) {|_, _| rand(-10.0..10.0)}
    pca = Ipca::Pca.new(m)
    eigenvalue, r = pca.first_principal_component
    puts "eigenvalue: #{eigenvalue}, r: #{r}"
    assert eigenvalue.kind_of? Numeric
    assert_equal Vector, r.class
  end
end

The program I'm trying to test is ipca.rb:

require "ipca/version"

module Ipca
  class Error < StandardError; end

  class Pca
    def initialize data
      @data = data.class == Matrix ? data : Matrix.rows(data)
    end

    # see https://en.wikipedia.org/wiki/Principal_component_analysis#Iterative_computation
    def first_principal_component(c = 100, tolerance = 0.001) # not sure whether defaults are apropos
      p = @data.column_vectors.count
      r = Vector.elements(Array.new(p) {|_| rand}).normalize
      eigenvalue = nil
      c.times do
        s = Vector.zero(p)
        @data.row_vectors.each do |x|
          s += x.dot(r)*x
        end
        eigenvalue = r.dot(s) # ?
        error = (eigenvalue*r-s).norm
        r = s.normalize
        exit if error < tolerance
      end
      return [eigenvalue, r]
    end
  end
end

(Git repo of entire tree)

Sometimes the test is successful, but much more often the test never gets to "Finished". In these cases there are zero or more dots representing (I assume) successful assertions. I'm guessing that the tests runs are being halted by some kind of timeout somewhere in my bundle, and that this times out intermittently because the input data for the test is of varying size. But if it's a timeout issue why is there not a message to that effect? Here is a series of test runs:

josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 44059

# Running:

..josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 57681

# Running:

.josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 57222

# Running:

..josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 7474

# Running:

..josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 1938

# Running:

..josie@josie-Inspiron-580:/var/www/html/ruby/ipca$ ruby -Itest test/ipca_test.rb Run options: --seed 61325

# Running:

..eigenvalue: 2027.687580111128, r: Vector[0.03288542301229099, -0.09533529249551115, 0.3033273986606458, 0.07951734565050736, 0.3575555246291426, 0.41614419068773545, 0.4928822662304588, 0.28785088479078025, 0.5144766379975693] .

Finished in 0.037173s, 80.7047 runs/s, 107.6063 assertions/s.

3 runs, 4 assertions, 0 failures, 0 errors, 0 skips josie@josie-Inspiron-580:/var/www/html/ruby/ipca$


Solution

  • Use break instead of exit:

    break if error < tolerance
    

    break exits just the do loop.

    exit exits the program itself not giving a chance for minitest to fail the test