require 'rails_helper' RSpec.describe UnitConversion do describe '.auto_unit' do it 'leaves units alone if reasonable' do expect(UnitConversion.auto_unit('1/2', 'tbsp')).to eq ['1/2', 'tbsp'] expect(UnitConversion.auto_unit('2', 'cups')).to eq ['2', 'cups'] expect(UnitConversion.auto_unit('1', 'c')).to eq ['1', 'c'] end it 'leaves units alone if unknown' do expect(UnitConversion.auto_unit('1/16', 'splat')).to eq ['1/16', 'splat'] expect(UnitConversion.auto_unit('4.5', 'dogs')).to eq ['4.5', 'dogs'] expect(UnitConversion.auto_unit('0', '')).to eq ['0', ''] end it 'converts standard volume units correctly' do expect(UnitConversion.auto_unit('6', 'tsp')).to eq ['2', 'tablespoon'] expect(UnitConversion.auto_unit('24', 'tsp')).to eq ['1/2', 'cup'] expect(UnitConversion.auto_unit('1/16', 'cup')).to eq ['1', 'tablespoon'] expect(UnitConversion.auto_unit('1/48', 'cup')).to eq ['1', 'teaspoon'] expect(UnitConversion.auto_unit('768', 'tsp')).to eq ['1', 'gallon'] expect(UnitConversion.auto_unit('6', '[tsp_us]')).to eq ['2', 'tablespoon'] end it 'converts standard mass units correctly' do expect(UnitConversion.auto_unit('32', 'oz')).to eq ['2', 'pound'] expect(UnitConversion.auto_unit('40', 'oz')).to eq ['2 1/2', 'pound'] expect(UnitConversion.auto_unit('3/4', 'lb')).to eq ['12', 'ounce'] expect(UnitConversion.auto_unit('0.0625', 'lb')).to eq ['1', 'ounce'] end end describe '.get_value' do it 'returns rationals' do expect(UnitConversion.get_value('1/2')).to eq Rational(1, 2) expect(UnitConversion.get_value('1 1/2')).to eq Rational(3, 2) expect(UnitConversion.get_value('-1/2')).to eq Rational(-1, 2) expect(UnitConversion.get_value('-1 1/2')).to eq Rational(-3, 2) expect(UnitConversion.get_value('18 9/10')).to eq Rational(189, 10) end it 'returns decimals' do expect(UnitConversion.get_value('-1')).to eq -1.to_d expect(UnitConversion.get_value('1.0')).to eq 1.to_d expect(UnitConversion.get_value('5.56')).to eq "5.56".to_d end end describe '.convert' do it 'scales decimal numbers' do expect(UnitConversion.convert('1', '2', 'cup', 'cup')).to eq '2' expect(UnitConversion.convert('1.5', '2', 'cup', 'cup')).to eq '3' expect(UnitConversion.convert('4', '.5', 'cup', 'cup')).to eq '2' expect(UnitConversion.convert('4', '1/2', 'cup', 'cup')).to eq '2' end it 'scales rational numbers' do expect(UnitConversion.convert('1/2', '2', 'cup', 'cup')).to eq '1' expect(UnitConversion.convert('1 1/2', '2', 'cup', 'cup')).to eq '3' expect(UnitConversion.convert('4', '.5', 'cup', 'cup')).to eq '2' expect(UnitConversion.convert('4', '1/2', 'cup', 'cup')).to eq '2' expect(UnitConversion.convert('2', '1/3', 'cup', 'cup')).to eq '2/3' end it 'converts units' do expect(UnitConversion.convert('1/2', '1', 'cup', 'tbsp')).to eq '8' expect(UnitConversion.convert('8', '1', 'tablespoon', 'cup')).to eq '1/2' expect(UnitConversion.convert('1', '1', 'tablespoon', 'cup')).to eq '1/16' expect(UnitConversion.convert('2.0', '1', 'tablespoon', 'cup')).to eq '0.125' expect(UnitConversion.convert('2/3', '1', 'tablespoon', 'teaspoons')).to eq '2' expect(UnitConversion.convert('2', '4', 'teaspoons', 'tablespoons')).to eq '2 2/3' end it 'scales odd units without conversion' do expect(UnitConversion.convert('1/2', '2', 'slices', 'slices')).to eq '1' expect(UnitConversion.convert('4', '1/8', nil, nil)).to eq '1/2' expect(UnitConversion.convert('4', '1/8', 'slices', nil)).to eq '1/2' expect(UnitConversion.convert('4', '1/8', nil, 'slices')).to eq '1/2' expect(UnitConversion.convert('4', '1/8', 'slices', '')).to eq '1/2' expect(UnitConversion.convert('4', '1/8', '', 'slices')).to eq '1/2' end it 'converts and scales' do expect(UnitConversion.convert('1/2', '2', 'cup', 'tbsp')).to eq '16' expect(UnitConversion.convert('2.0', '1 1/2', 'tablespoon', 'cup')).to eq '0.188' expect(UnitConversion.convert('2', '1 1/2', 'tablespoon', 'cup')).to eq '3/16' end it 'converts from volume to mass' do expect(UnitConversion.convert('5', '1', 'cup', 'ounce', '5 oz/c')).to eq '25' end it 'converts from mass to volume' do expect(UnitConversion.convert('25', '1', 'ounce', 'cup', '5 oz/c')).to eq '5' end end describe '.parse' do it 'correctly parses strings to Units' do data = { '4 c' => Unitwise(4, 'cup'), '5.5 oz' => Unitwise(5.5, 'ounce'), '-4 tbsp' => Unitwise(-4, 'tablespoon'), '223 g/c' => Unitwise(223, 'gram/cup'), '1/3 c' => Unitwise("1/3".to_r, 'cup'), '-5/16 g' => Unitwise("-5/16".to_r, 'gram'), '2 1/2 c' => Unitwise("5/2".to_r, 'cup'), '1.03 gram/ml' => Unitwise(1.03, 'gram/milliliter') } data.each do |input, output| expect(UnitConversion.parse(input)).to eq output end end it 'raises UnknownUnitError with bad units' do data = [ '5 cats', '33 gulps/thinking', '9.2 meeters/s2', '5/8 floor' ] data.each do |input| expect { UnitConversion.parse(input) }.to raise_error(UnitConversion::UnknownUnitError), "'#{input}' didn't raise" end end it 'raises UnparseableUnitError on malformed string' do data = [ '55', '55.5', '-55', '-55.55', '5.5/2 cups', '2/3.0 cups', 'ounce', '-.4 cups', 'gallons 6', '5,5 tsp' ] data.each do |input| expect { UnitConversion.parse(input) }.to raise_error(UnitConversion::UnparseableUnitError), "'#{input}' didn't raise" end end end describe '.density?' do it 'returns true for any mass over volume unit' do data = [ Unitwise(1, 'gram/cup'), Unitwise(1, 'pound/gallon'), Unitwise(1, 'ounce/tablespoon'), Unitwise(1, 'ounce/centimeter3') ] data.each do |input| expect(UnitConversion.density?(input)).to be_truthy end end it 'returns false for any non density unit' do data = [ Unitwise(1, 'cup'), Unitwise(1, 'gram'), Unitwise(1, 'gram/hour'), Unitwise(1, 'centimeter3/ounce') ] data.each do |input| expect(UnitConversion.density?(input)).to be_falsey end end end describe '.rationalize' do it 'leaves integers alone' do expect(UnitConversion.rationalize(1)).to eq 1 expect(UnitConversion.rationalize(15)).to eq 15 expect(UnitConversion.rationalize(-1)).to eq -1 expect(UnitConversion.rationalize(0)).to eq 0 end it 'leaves non-fractional numbers alone' do expect(UnitConversion.rationalize(1.0)).to eq 1.0 expect(UnitConversion.rationalize(-1.0)).to eq -1.0 expect(UnitConversion.rationalize(0.0)).to eq 0.0 expect(UnitConversion.rationalize(35.0)).to eq 35.0 end it 'leaves already nice rationals alone' do expect(UnitConversion.rationalize(Rational(1,2))).to eq Rational(1,2) expect(UnitConversion.rationalize(Rational(5,2))).to eq Rational(5,2) expect(UnitConversion.rationalize(Rational(3,16))).to eq Rational(3,16) expect(UnitConversion.rationalize(Rational(3,4))).to eq Rational(3,4) end it 'converts neat decimals to rationals' do expect(UnitConversion.rationalize(1.5)).to eq Rational(3,2) expect(UnitConversion.rationalize(0.125)).to eq Rational(1,8) expect(UnitConversion.rationalize(5.0625)).to eq Rational(81, 16) expect(UnitConversion.rationalize(0.75)).to eq Rational(3,4) end it 'rounds weird rationals to nice rationals' do expect(UnitConversion.rationalize(Rational(3,7))).to eq Rational(7,16) expect(UnitConversion.rationalize(Rational(2,5))).to eq Rational(3,8) expect(UnitConversion.rationalize(Rational(2,5))).to eq Rational(3,8) end it 'rounds weird decimals to nice rationals' do expect(UnitConversion.rationalize(0.24)).to eq Rational(1,4) expect(UnitConversion.rationalize(1.24)).to eq Rational(5,4) expect(UnitConversion.rationalize(1.13)).to eq Rational(9,8) end end describe '.normalize_unit_name' do it 'converts simple units' do data = { 'c' => '[cup_us]', 'cups' => '[cup_us]', 'pints' => '[pt_us]', 'gram' => 'g', 'grams' => 'g', 'Grams' => 'g', 'Tbsp' => '[tbs_us]' } data.each do |input, output| expect(UnitConversion.normalize_unit_names(input)).to eq output end end it 'converts mixed units' do data = { 'oz/c' => '[oz_av]/[cup_us]', 'kilograms/cups' => 'kg/[cup_us]', 'pints/junk' => '[pt_us]/junk' } data.each do |input, output| expect(UnitConversion.normalize_unit_names(input)).to eq output end end end end