@@ -36,6 +36,69 @@ def with_temp_character_format_vcf
3636 end
3737 end
3838
39+ def with_temp_bcf
40+ Tempfile . create ( [ "format_test" , ".bcf" ] ) do |file |
41+ path = file . path
42+ file . close
43+
44+ header = HTS ::Bcf ::Header . new
45+ header . set_version ( "VCFv4.3" )
46+ header . append ( "##contig=<ID=1,length=100>" )
47+ header . append ( '##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">' )
48+ header . append ( '##FORMAT=<ID=PL,Number=G,Type=Integer,Description="Phred likelihoods">' )
49+ header . append ( '##FORMAT=<ID=MISSI,Number=1,Type=Integer,Description="defined but absent integer">' )
50+ header . append ( '##FORMAT=<ID=MISSF,Number=1,Type=Float,Description="defined but absent float">' )
51+ header . append ( '##FORMAT=<ID=IV,Number=.,Type=Integer,Description="integer with sentinels">' )
52+ header . append ( '##FORMAT=<ID=FV,Number=.,Type=Float,Description="float with sentinels">' )
53+ header . add_sample ( "S1" , sync : false )
54+ header . add_sample ( "S2" , sync : true )
55+
56+ HTS ::Bcf . open ( path , "wb" ) do |bcf |
57+ bcf . write_header ( header )
58+
59+ record = HTS ::Bcf ::Record . new ( header )
60+ record . rid = HTS ::LibHTS . bcf_hdr_name2id ( header . struct , "1" )
61+ record . pos = 9
62+
63+ rc = HTS ::LibHTS . bcf_update_alleles_str ( header . struct , record . struct , "A,C" )
64+ raise "bcf_update_alleles_str failed (rc=#{ rc } )" if rc < 0
65+
66+ genotypes = [
67+ HTS ::LibHTS . bcf_gt_unphased ( 0 ) ,
68+ HTS ::LibHTS . bcf_gt_unphased ( 1 ) ,
69+ HTS ::LibHTS . bcf_gt_unphased ( 1 ) ,
70+ HTS ::LibHTS . bcf_gt_unphased ( 1 )
71+ ]
72+ genotype_ptr = FFI ::MemoryPointer . new ( :int32 , genotypes . size )
73+ genotype_ptr . write_array_of_int32 ( genotypes )
74+ rc = HTS ::LibHTS . bcf_update_genotypes ( header . struct , record . struct , genotype_ptr , genotypes . size )
75+ raise "bcf_update_genotypes failed (rc=#{ rc } )" if rc < 0
76+
77+ likelihoods = [ 10 , 20 , 30 , 40 , 50 , 60 ]
78+ likelihood_ptr = FFI ::MemoryPointer . new ( :int32 , likelihoods . size )
79+ likelihood_ptr . write_array_of_int32 ( likelihoods )
80+ rc = HTS ::LibHTS . bcf_update_format_int32 ( header . struct , record . struct , "PL" , likelihood_ptr , likelihoods . size )
81+ raise "bcf_update_format_int32 failed (rc=#{ rc } )" if rc < 0
82+
83+ int_with_sentinels = [ 10 , HTS ::LibHTS . bcf_int32_vector_end , HTS ::LibHTS . bcf_int32_missing , HTS ::LibHTS . bcf_int32_vector_end ]
84+ int_ptr = FFI ::MemoryPointer . new ( :int32 , int_with_sentinels . size )
85+ int_ptr . write_array_of_int32 ( int_with_sentinels )
86+ rc = HTS ::LibHTS . bcf_update_format_int32 ( header . struct , record . struct , "IV" , int_ptr , int_with_sentinels . size )
87+ raise "bcf_update_format_int32 failed for IV (rc=#{ rc } )" if rc < 0
88+
89+ float_words = [ 0x3fc0_0000 , 0x7f80_0002 , 0x7f80_0001 , 0x7f80_0002 ]
90+ float_ptr = FFI ::MemoryPointer . new ( :uint32 , float_words . size )
91+ float_ptr . write_array_of_uint32 ( float_words )
92+ rc = HTS ::LibHTS . bcf_update_format_float ( header . struct , record . struct , "FV" , float_ptr , float_words . size )
93+ raise "bcf_update_format_float failed for FV (rc=#{ rc } )" if rc < 0
94+
95+ bcf . write ( record )
96+ end
97+
98+ yield path
99+ end
100+ end
101+
39102 def with_temp_gt_vcf
40103 Tempfile . create ( [ "format_gt" , ".vcf" ] ) do |file |
41104 file . write <<~VCF
@@ -112,13 +175,15 @@ def test_get_like_crystal
112175 def test_get_without_type
113176 assert_equal [ 409 , 409 ] , @fmt . get ( "GQ" )
114177 assert_equal [ 35 , 35 ] , @fmt . get ( "DP" )
115- assert_equal [ -20.0 , -5.0 , -20.0 , -20.0 , -5.0 , -20.0 ] , @fmt . get ( "GL" )
178+ assert_equal [ [ -20.0 , -5.0 , -20.0 ] , [ -20.0 , -5.0 , -20.0 ] ] , @fmt . get ( "GL" )
179+ assert_equal [ "0/1" , "0/1" ] , @fmt . get ( "GT" )
116180 end
117181
118182 def test_get_square_brackets
119183 assert_equal [ 409 , 409 ] , @fmt [ "GQ" ]
120184 assert_equal [ 35 , 35 ] , @fmt [ "DP" ]
121- assert_equal [ -20.0 , -5.0 , -20.0 , -20.0 , -5.0 , -20.0 ] , @fmt [ "GL" ]
185+ assert_equal [ [ -20.0 , -5.0 , -20.0 ] , [ -20.0 , -5.0 , -20.0 ] ] , @fmt [ "GL" ]
186+ assert_equal [ "0/1" , "0/1" ] , @fmt [ "GT" ]
122187 end
123188
124189 def test_get_unknown_key
@@ -189,13 +254,55 @@ def test_fields
189254
190255 def test_to_h
191256 assert_equal (
192- # FIXME: Maybe GT should be string?
193- { "GT" => [ 2 , 4 , 2 , 4 ] , "GQ" => [ 409 , 409 ] , "DP" => [ 35 , 35 ] ,
194- "GL" => [ -20.0 , -5.0 , -20.0 , -20.0 , -5.0 , -20.0 ] } ,
257+ { "GT" => [ "0/1" , "0/1" ] , "GQ" => [ 409 , 409 ] , "DP" => [ 35 , 35 ] ,
258+ "GL" => [ [ -20.0 , -5.0 , -20.0 ] , [ -20.0 , -5.0 , -20.0 ] ] } ,
195259 @fmt . to_h
196260 )
197261 end
198262
263+ def test_get_high_level_shapes_values_by_sample
264+ with_temp_bcf do |path |
265+ HTS ::Bcf . open ( path ) do |bcf |
266+ format = bcf . first . format
267+
268+ assert_equal [ "0/1" , "1/1" ] , format . get ( "GT" )
269+ assert_equal [ [ 10 , 20 , 30 ] , [ 40 , 50 , 60 ] ] , format . get ( "PL" )
270+ assert_equal [ [ 10 ] , [ nil ] ] , format . get ( "IV" )
271+
272+ floats = format . get ( "FV" )
273+ assert_equal 2 , floats . size
274+ assert_equal [ 1.5 ] , floats [ 0 ]
275+ assert_equal [ nil ] , floats [ 1 ]
276+ assert_nil format . get ( "MISSI" )
277+ assert_nil format . get ( "MISSF" )
278+ end
279+ end
280+ end
281+
282+ def test_get_raw_preserves_flat_numeric_buffers
283+ with_temp_bcf do |path |
284+ HTS ::Bcf . open ( path ) do |bcf |
285+ format = bcf . first . format
286+
287+ assert_equal [ 10 , 20 , 30 , 40 , 50 , 60 ] , format . get_raw ( "PL" )
288+
289+ ints = format . get_raw ( "IV" )
290+ assert_equal 4 , ints . size
291+ assert_equal 10 , ints [ 0 ]
292+ assert_equal HTS ::LibHTS . bcf_int32_vector_end , ints [ 1 ]
293+ assert_equal HTS ::LibHTS . bcf_int32_missing , ints [ 2 ]
294+ assert_equal HTS ::LibHTS . bcf_int32_vector_end , ints [ 3 ]
295+
296+ floats = format . get_raw ( "FV" )
297+ assert_equal 4 , floats . size
298+ assert_in_delta 1.5 , floats [ 0 ] , 0.001
299+ assert_predicate floats [ 1 ] , :nan?
300+ assert_predicate floats [ 2 ] , :nan?
301+ assert_predicate floats [ 3 ] , :nan?
302+ end
303+ end
304+ end
305+
199306 def test_update_methods_round_trip
200307 with_temp_format_source_vcf do |source_path |
201308 with_temp_output_path do |output_path |
0 commit comments