From 21c82d4f2e59ac7431eb32307c5789ad0246b42d Mon Sep 17 00:00:00 2001 From: George Pimm Date: Tue, 6 Jun 2023 15:14:27 +0000 Subject: [PATCH] Github 41: Add errors for using invalid run info ids --- CHANGELOG.md | 5 + c++/pod5_format/c_api.cpp | 6 +- c++/pod5_format/dictionary_writer.h | 2 + c++/pod5_format/expandable_buffer.h | 5 +- c++/pod5_format/file_writer.cpp | 21 + c++/pod5_format/read_table_writer_utils.cpp | 3 + c++/test/c_api_tests.cpp | 483 ++++++++++++++------ docs/requirements.txt | 2 +- python/pod5/pyproject.toml | 2 +- 9 files changed, 377 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b28c015..6d1759b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ All notable changes, updates, and fixes to pod5 will be documented here The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.2] 2023-06-06 + +### Fixed +- Add API error when adding reads with invalid end reason, pore type or run info. + ## [0.2.1] 2023-05-25 ### Changed diff --git a/c++/pod5_format/c_api.cpp b/c++/pod5_format/c_api.cpp index 0f38007..e20b692 100644 --- a/c++/pod5_format/c_api.cpp +++ b/c++/pod5_format/c_api.cpp @@ -1058,8 +1058,10 @@ inline bool load_struct_row_into_read_data( end_reason_internal = pod5::ReadEndReason::signal_negative; break; default: - pod5_set_error( - arrow::Status::Invalid("out of range end reason passed to pod5_add_end_reason")); + pod5_set_error(arrow::Status::Invalid( + "out of range end reason ", + typed_row_data->end_reason[row_id], + " passed to add read")); return false; } diff --git a/c++/pod5_format/dictionary_writer.h b/c++/pod5_format/dictionary_writer.h index ac3d51e..6fb07b3 100644 --- a/c++/pod5_format/dictionary_writer.h +++ b/c++/pod5_format/dictionary_writer.h @@ -17,6 +17,8 @@ class POD5_FORMAT_EXPORT DictionaryWriter { std::shared_ptr const & indices); virtual pod5::Result> get_value_array() = 0; virtual std::size_t item_count() = 0; + + bool is_valid(std::size_t value) { return value < item_count(); } }; } // namespace pod5 diff --git a/c++/pod5_format/expandable_buffer.h b/c++/pod5_format/expandable_buffer.h index 7a14004..234028e 100644 --- a/c++/pod5_format/expandable_buffer.h +++ b/c++/pod5_format/expandable_buffer.h @@ -43,7 +43,10 @@ class ExpandableBuffer { gsl::span get_data_span() const { - assert(m_buffer); + if (!m_buffer) { + return {}; + } + return gsl::make_span(m_buffer->data(), m_buffer->size()).template as_span(); } diff --git a/c++/pod5_format/file_writer.cpp b/c++/pod5_format/file_writer.cpp index 32ad762..865d109 100644 --- a/c++/pod5_format/file_writer.cpp +++ b/c++/pod5_format/file_writer.cpp @@ -85,6 +85,8 @@ class FileWriterImpl { return arrow::Status::Invalid("File writer closed, cannot write further data"); } + ARROW_RETURN_NOT_OK(check_read(read_data)); + ARROW_ASSIGN_OR_RAISE( std::vector signal_rows, add_signal(read_data.read_id, signal)); @@ -103,12 +105,31 @@ class FileWriterImpl { return arrow::Status::Invalid("File writer closed, cannot write further data"); } + ARROW_RETURN_NOT_OK(check_read(read_data)); + // Write read data and signal row entries: auto read_table_row = m_read_table_writer->add_read(read_data, signal_rows, signal_duration); return read_table_row.status(); } + arrow::Status check_read(ReadData const & read_data) + { + if (!m_read_table_dict_writers.run_info_writer->is_valid(read_data.run_info)) { + return arrow::Status::Invalid("Invalid run info passed to add_read"); + } + + if (!m_read_table_dict_writers.pore_writer->is_valid(read_data.pore_type)) { + return arrow::Status::Invalid("Invalid pore type passed to add_read"); + } + + if (!m_read_table_dict_writers.end_reason_writer->is_valid(read_data.end_reason)) { + return arrow::Status::Invalid("Invalid end reason passed to add_read"); + } + + return arrow::Status::OK(); + } + pod5::Result> add_signal( boost::uuids::uuid const & read_id, gsl::span const & signal) diff --git a/c++/pod5_format/read_table_writer_utils.cpp b/c++/pod5_format/read_table_writer_utils.cpp index 9f6abd6..96b68ec 100644 --- a/c++/pod5_format/read_table_writer_utils.cpp +++ b/c++/pod5_format/read_table_writer_utils.cpp @@ -28,6 +28,9 @@ arrow::Result> get_array_data( std::size_t expected_length) { auto const value_data = builder.get_string_data(); + if (!value_data) { + return Status::Invalid("Missing array value data for dictionary"); + } arrow::TypedBufferBuilder offset_builder; auto const & offset_data = builder.get_typed_offset_data(); diff --git a/c++/test/c_api_tests.cpp b/c++/test/c_api_tests.cpp index 3aebad9..4d4a728 100644 --- a/c++/test/c_api_tests.cpp +++ b/c++/test/c_api_tests.cpp @@ -13,6 +13,41 @@ #include #include +struct Pod5C_Result { + static Pod5C_Result capture(pod5_error_t err_num) + { + return Pod5C_Result{err_num, pod5_get_error_string()}; + } + + pod5_error_t error_code; + std::string error_string; +}; + +namespace Catch { +template <> +struct StringMaker { + static std::string convert(Pod5C_Result const & value) + { + return "{ code: " + std::to_string(value.error_code) + "| " + value.error_string + " }"; + } +}; +} // namespace Catch + +class IsPod5COk : public Catch::MatcherBase { +public: + IsPod5COk() = default; + + bool match(Pod5C_Result const & result) const override { return result.error_code == POD5_OK; } + + virtual std::string describe() const override { return "== POD5_OK"; } +}; + +#define CHECK_POD5_OK(statement) \ + { \ + auto const & _res = (statement); \ + CHECK_THAT(Pod5C_Result::capture(_res), IsPod5COk()); \ + } + struct Pod5ReadId { Pod5ReadId() = default; @@ -69,7 +104,7 @@ SCENARIO("C API Reads") // Write the file: { - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); CHECK(!pod5_create_file(NULL, "c_software", NULL)); CHECK(pod5_get_error_no() == POD5_ERROR_INVALID); CHECK(!pod5_create_file("", "c_software", NULL)); @@ -81,10 +116,10 @@ SCENARIO("C API Reads") auto file = pod5_create_file(filename, "c_software", NULL); REQUIRE(file); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); std::int16_t pore_type_id = -1; - CHECK(pod5_add_pore(&pore_type_id, file, "pore_type") == POD5_OK); + CHECK_POD5_OK(pod5_add_pore(&pore_type_id, file, "pore_type")); CHECK(pore_type_id == 0); std::vector context_tags_keys{"thing", "foo"}; @@ -92,38 +127,6 @@ SCENARIO("C API Reads") std::vector tracking_id_keys{"baz", "other"}; std::vector tracking_id_values{"baz_val", "other_val"}; - std::int16_t run_info_id = -1; - CHECK( - pod5_add_run_info( - &run_info_id, - file, - "acquisition_id", - 15400, - adc_max, - adc_min, - context_tags_keys.size(), - context_tags_keys.data(), - context_tags_values.data(), - "experiment_name", - "flow_cell_id", - "flow_cell_product_code", - "protocol_name", - "protocol_run_id", - 200000, - "sample_id", - 4000, - "sequencing_kit", - "sequencer_position", - "sequencer_position_type", - "software", - "system_name", - "system_type", - tracking_id_keys.size(), - tracking_id_keys.data(), - tracking_id_values.data()) - == POD5_OK); - CHECK(run_info_id == 0); - std::uint32_t read_number = 12; std::uint64_t start_sample = 10245; float median_before = 200.0f; @@ -133,6 +136,7 @@ SCENARIO("C API Reads") uint8_t end_reason_forced = false; auto read_id_array = (read_id_t const *)input_read_id.begin(); + std::int16_t run_info_id = 0; ReadBatchRowInfoArrayV3 row_data{ read_id_array, &read_number, @@ -154,14 +158,48 @@ SCENARIO("C API Reads") &num_reads_since_mux_change, &time_since_mux_change}; - { - std::int16_t const * signal_arr[] = {signal_1.data()}; - std::uint32_t signal_size[] = {(std::uint32_t)signal_1.size()}; + std::int16_t const * signal_arr[] = {signal_1.data()}; + std::uint32_t signal_size[] = {(std::uint32_t)signal_1.size()}; - CHECK( - pod5_add_reads_data( - file, 1, READ_BATCH_ROW_INFO_VERSION_3, &row_data, signal_arr, signal_size) - == POD5_OK); + // Referencing a non-existent run id should fail: + CHECK( + pod5_add_reads_data( + file, 1, READ_BATCH_ROW_INFO_VERSION_3, &row_data, signal_arr, signal_size) + == POD5_ERROR_INVALID); + + // Now actually add the run info: + CHECK_POD5_OK(pod5_add_run_info( + &run_info_id, + file, + "acquisition_id", + 15400, + adc_max, + adc_min, + context_tags_keys.size(), + context_tags_keys.data(), + context_tags_values.data(), + "experiment_name", + "flow_cell_id", + "flow_cell_product_code", + "protocol_name", + "protocol_run_id", + 200000, + "sample_id", + 4000, + "sequencing_kit", + "sequencer_position", + "sequencer_position_type", + "software", + "system_name", + "system_type", + tracking_id_keys.size(), + tracking_id_keys.data(), + tracking_id_values.data())); + CHECK(run_info_id == 0); + + { + CHECK_POD5_OK(pod5_add_reads_data( + file, 1, READ_BATCH_ROW_INFO_VERSION_3, &row_data, signal_arr, signal_size)); read_count += 1; } @@ -182,34 +220,32 @@ SCENARIO("C API Reads") auto read_id_array = (read_id_t const *)input_read_id_2.begin(); row_data.read_id = read_id_array; - CHECK( - pod5_add_reads_data_pre_compressed( - file, - 1, - READ_BATCH_ROW_INFO_VERSION_3, - &row_data, - &compressed_data_ptr, - &compressed_size_ptr, - &signal_size_ptr, - &signal_counts) - == POD5_OK); + CHECK_POD5_OK(pod5_add_reads_data_pre_compressed( + file, + 1, + READ_BATCH_ROW_INFO_VERSION_3, + &row_data, + &compressed_data_ptr, + &compressed_size_ptr, + &signal_size_ptr, + &signal_counts)); read_count += 1; } - CHECK(pod5_close_and_free_writer(file) == POD5_OK); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_close_and_free_writer(file)); + CHECK_POD5_OK(pod5_get_error_no()); } // Read the file back: { - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); CHECK(!pod5_open_file(NULL)); auto file = pod5_open_file(filename); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); CHECK(!!file); FileInfo_t file_info; - CHECK(pod5_get_file_info(file, &file_info) == POD5_OK); + CHECK_POD5_OK(pod5_get_file_info(file, &file_info)); CHECK(file_info.version.major == pod5::Pod5MajorVersion); CHECK(file_info.version.minor == pod5::Pod5MinorVersion); CHECK(file_info.version.revision == pod5::Pod5RevVersion); @@ -224,21 +260,21 @@ SCENARIO("C API Reads") } std::size_t read_count = 0; - CHECK(pod5_get_read_count(file, &read_count) == POD5_OK); + CHECK_POD5_OK(pod5_get_read_count(file, &read_count)); REQUIRE(read_count == 2); std::vector read_ids(2); CHECK(pod5_get_read_ids(file, 1, (read_id_t *)read_ids.data()) != POD5_OK); - CHECK(pod5_get_read_ids(file, read_ids.size(), (read_id_t *)read_ids.data()) == POD5_OK); + CHECK_POD5_OK(pod5_get_read_ids(file, read_ids.size(), (read_id_t *)read_ids.data())); std::vector expected_read_ids{input_read_id, input_read_id_2}; CHECK(read_ids == expected_read_ids); std::size_t batch_count = 0; - CHECK(pod5_get_read_batch_count(&batch_count, file) == POD5_OK); + CHECK_POD5_OK(pod5_get_read_batch_count(&batch_count, file)); REQUIRE(batch_count == 1); Pod5ReadRecordBatch * batch_0 = nullptr; - CHECK(pod5_get_read_batch(&batch_0, file, 0) == POD5_OK); + CHECK_POD5_OK(pod5_get_read_batch(&batch_0, file, 0)); REQUIRE(!!batch_0); for (std::size_t row = 0; row < read_count; ++row) { @@ -253,14 +289,12 @@ SCENARIO("C API Reads") ReadBatchRowInfoV3 v3_struct; uint16_t input_version = 0; - CHECK( - pod5_get_read_batch_row_info_data( - batch_0, row, READ_BATCH_ROW_INFO_VERSION, &v3_struct, &input_version) - == POD5_OK); + CHECK_POD5_OK(pod5_get_read_batch_row_info_data( + batch_0, row, READ_BATCH_ROW_INFO_VERSION, &v3_struct, &input_version)); CHECK(input_version == 3); std::string formatted_uuid(36, '\0'); - CHECK(pod5_format_read_id(v3_struct.read_id, &formatted_uuid[0]) == POD5_OK); + CHECK_POD5_OK(pod5_format_read_id(v3_struct.read_id, &formatted_uuid[0])); CHECK( formatted_uuid.size() == boost::uuids::to_string(*(boost::uuids::uuid *)v3_struct.read_id).size()); @@ -290,43 +324,34 @@ SCENARIO("C API Reads") CHECK(v3_struct.num_samples == signal.size()); std::vector signal_row_indices(v3_struct.signal_row_count); - CHECK( - pod5_get_signal_row_indices( - batch_0, row, signal_row_indices.size(), signal_row_indices.data()) - == POD5_OK); + CHECK_POD5_OK(pod5_get_signal_row_indices( + batch_0, row, signal_row_indices.size(), signal_row_indices.data())); std::vector signal_row_info(v3_struct.signal_row_count); - CHECK( - pod5_get_signal_row_info( - file, - signal_row_indices.size(), - signal_row_indices.data(), - signal_row_info.data()) - == POD5_OK); + CHECK_POD5_OK(pod5_get_signal_row_info( + file, + signal_row_indices.size(), + signal_row_indices.data(), + signal_row_info.data())); std::vector read_signal(signal_row_info.front()->stored_sample_count); REQUIRE(signal_row_info.front()->stored_sample_count == signal.size()); - CHECK( - pod5_get_signal( - file, - signal_row_info.front(), - signal_row_info.front()->stored_sample_count, - read_signal.data()) - == POD5_OK); + CHECK_POD5_OK(pod5_get_signal( + file, + signal_row_info.front(), + signal_row_info.front()->stored_sample_count, + read_signal.data())); CHECK(read_signal == signal); std::size_t sample_count = 0; - CHECK( - pod5_get_read_complete_sample_count(file, batch_0, row, &sample_count) == POD5_OK); + CHECK_POD5_OK(pod5_get_read_complete_sample_count(file, batch_0, row, &sample_count)); CHECK(sample_count == signal_row_info.front()->stored_sample_count); - CHECK( - pod5_get_read_complete_signal(file, batch_0, row, sample_count, read_signal.data()) - == POD5_OK); + CHECK_POD5_OK(pod5_get_read_complete_signal( + file, batch_0, row, sample_count, read_signal.data())); CHECK(read_signal == signal); - CHECK( - pod5_free_signal_row_info(signal_row_indices.size(), signal_row_info.data()) - == POD5_OK); + CHECK_POD5_OK( + pod5_free_signal_row_info(signal_row_indices.size(), signal_row_info.data())); std::string expected_pore_type{"pore_type"}; std::array char_buffer{}; @@ -340,10 +365,8 @@ SCENARIO("C API Reads") } { returned_size = char_buffer.size(); - CHECK( - pod5_get_pore_type( - batch_0, v3_struct.pore_type, char_buffer.data(), &returned_size) - == POD5_OK); + CHECK_POD5_OK(pod5_get_pore_type( + batch_0, v3_struct.pore_type, char_buffer.data(), &returned_size)); CHECK(returned_size == expected_pore_type.size() + 1); CHECK(std::string{char_buffer.data()} == expected_pore_type); } @@ -365,28 +388,25 @@ SCENARIO("C API Reads") { returned_size = char_buffer.size(); pod5_end_reason end_reason = POD5_END_REASON_UNKNOWN; - CHECK( - pod5_get_end_reason( - batch_0, - v3_struct.end_reason, - &end_reason, - char_buffer.data(), - &returned_size) - == POD5_OK); + CHECK_POD5_OK(pod5_get_end_reason( + batch_0, + v3_struct.end_reason, + &end_reason, + char_buffer.data(), + &returned_size)); CHECK(returned_size == expected_end_reason.size() + 1); CHECK(end_reason == POD5_END_REASON_MUX_CHANGE); CHECK(std::string{char_buffer.data()} == expected_end_reason); } CalibrationExtraData calibration_extra_data{}; - CHECK( - pod5_get_calibration_extra_info(batch_0, row, &calibration_extra_data) == POD5_OK); + CHECK_POD5_OK(pod5_get_calibration_extra_info(batch_0, row, &calibration_extra_data)); CHECK(calibration_extra_data.digitisation == adc_max - adc_min + 1); CHECK(calibration_extra_data.range == 8192 * calibration_scale); } run_info_index_t run_info_count = 0; - CHECK(pod5_get_file_run_info_count(file, &run_info_count) == POD5_OK); + CHECK_POD5_OK(pod5_get_file_run_info_count(file, &run_info_count)); REQUIRE(run_info_count == 1); auto check_run_info = [](RunInfoDictData * run_info) { @@ -404,19 +424,190 @@ SCENARIO("C API Reads") }; RunInfoDictData * run_info_data_out_1 = nullptr; - CHECK(pod5_get_file_run_info(file, 0, &run_info_data_out_1) == POD5_OK); + CHECK_POD5_OK(pod5_get_file_run_info(file, 0, &run_info_data_out_1)); check_run_info(run_info_data_out_1); pod5_free_run_info(run_info_data_out_1); RunInfoDictData * run_info_data_out_2 = nullptr; - CHECK(pod5_get_run_info(batch_0, 0, &run_info_data_out_2) == POD5_OK); + CHECK_POD5_OK(pod5_get_run_info(batch_0, 0, &run_info_data_out_2)); check_run_info(run_info_data_out_2); pod5_free_run_info(run_info_data_out_2); pod5_free_read_batch(batch_0); pod5_close_and_free_reader(file); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); + } +} + +SCENARIO("C API Many Reads") +{ + static constexpr char const * filename = "./foo_c_api.pod5"; + + pod5_init(); + auto fin = gsl::finally([] { pod5_terminate(); }); + + auto uuid_gen = boost::uuids::random_generator_mt19937(); + auto input_read_id = uuid_gen(); + auto input_read_id_2 = uuid_gen(); + std::vector signal_1(10); + std::iota(signal_1.begin(), signal_1.end(), -20000); + + std::vector signal_2(20); + std::iota(signal_2.begin(), signal_2.end(), 0); + + std::size_t read_count = 10037; + + std::int16_t adc_min = -4096; + std::int16_t adc_max = 4095; + + // Write the file: + { + CHECK_POD5_OK(pod5_get_error_no()); + CHECK(!pod5_create_file(NULL, "c_software", NULL)); + CHECK(pod5_get_error_no() == POD5_ERROR_INVALID); + CHECK(!pod5_create_file("", "c_software", NULL)); + CHECK(pod5_get_error_no() == POD5_ERROR_INVALID); + CHECK(!pod5_create_file("", NULL, NULL)); + CHECK(pod5_get_error_no() == POD5_ERROR_INVALID); + + REQUIRE(remove_file_if_exists(filename).ok()); + + auto file = pod5_create_file(filename, "c_software", NULL); + REQUIRE(file); + CHECK_POD5_OK(pod5_get_error_no()); + + std::int16_t pore_type_id = -1; + CHECK_POD5_OK(pod5_add_pore(&pore_type_id, file, "pore_type")); + CHECK(pore_type_id == 0); + + std::vector context_tags_keys{"thing", "foo"}; + std::vector context_tags_values{"thing_val", "foo_val"}; + std::vector tracking_id_keys{"baz", "other"}; + std::vector tracking_id_values{"baz_val", "other_val"}; + + std::int16_t run_info_id = -1; + CHECK_POD5_OK(pod5_add_run_info( + &run_info_id, + file, + "acquisition_id", + 15400, + adc_max, + adc_min, + context_tags_keys.size(), + context_tags_keys.data(), + context_tags_values.data(), + "experiment_name", + "flow_cell_id", + "flow_cell_product_code", + "protocol_name", + "protocol_run_id", + 200000, + "sample_id", + 4000, + "sequencing_kit", + "sequencer_position", + "sequencer_position_type", + "software", + "system_name", + "system_type", + tracking_id_keys.size(), + tracking_id_keys.data(), + tracking_id_values.data())); + CHECK(run_info_id == 0); + + std::vector read_number(read_count, 12); + std::vector start_sample(read_count, 10245); + std::vector median_before(read_count, 200.0f); + std::vector channel(read_count, 43); + std::vector well(read_count, 4); + std::vector end_reason(read_count, POD5_END_REASON_MUX_CHANGE); + std::vector end_reason_forced(read_count, false); + std::vector read_id_array(read_count, input_read_id); + + std::vector calibration_offset(read_count, 54.0f); + std::vector calibration_scale(read_count, 100.0f); + + std::vector predicted_scale(read_count, 2.3f); + std::vector predicted_shift(read_count, 10.0f); + std::vector tracked_scale(read_count, 4.3f); + std::vector tracked_shift(read_count, 15.0f); + std::vector num_reads_since_mux_change(read_count, 1234); + std::vector time_since_mux_change(read_count, 2.4f); + std::vector num_minknow_events(read_count, 104); + + std::vector pore_type_ids(read_count, pore_type_id); + std::vector run_info_ids(read_count, run_info_id); + + std::vector signal_arr; + std::vector signal_size; + ReadBatchRowInfoArrayV3 row_data{ + (read_id_t *)read_id_array.data(), + read_number.data(), + start_sample.data(), + median_before.data(), + channel.data(), + well.data(), + pore_type_ids.data(), + calibration_offset.data(), + calibration_scale.data(), + end_reason.data(), + end_reason_forced.data(), + run_info_ids.data(), + num_minknow_events.data(), + tracked_scale.data(), + tracked_shift.data(), + predicted_scale.data(), + predicted_shift.data(), + num_reads_since_mux_change.data(), + time_since_mux_change.data()}; + + for (std::size_t i = 0; i < read_count; ++i) { + signal_arr.push_back(signal_1.data()); + signal_size.push_back((std::uint32_t)signal_1.size()); + } + + CHECK_POD5_OK(pod5_add_reads_data( + file, + read_count, + READ_BATCH_ROW_INFO_VERSION_3, + &row_data, + signal_arr.data(), + signal_size.data())); + + CHECK_POD5_OK(pod5_close_and_free_writer(file)); + CHECK_POD5_OK(pod5_get_error_no()); + } + + // Read the file back: + { + CHECK_POD5_OK(pod5_get_error_no()); + CHECK(!pod5_open_file(NULL)); + auto file = pod5_open_file(filename); + CHECK_POD5_OK(pod5_get_error_no()); + CHECK(!!file); + + FileInfo_t file_info; + CHECK_POD5_OK(pod5_get_file_info(file, &file_info)); + CHECK(file_info.version.major == pod5::Pod5MajorVersion); + CHECK(file_info.version.minor == pod5::Pod5MinorVersion); + CHECK(file_info.version.revision == pod5::Pod5RevVersion); + { + auto reader = pod5::open_file_reader(filename); + boost::uuids::uuid file_identifier; + std::copy( + file_info.file_identifier, + file_info.file_identifier + sizeof(file_info.file_identifier), + file_identifier.begin()); + CHECK(file_identifier == (*reader)->schema_metadata().file_identifier); + } + + std::size_t read_count_returned = 0; + CHECK_POD5_OK(pod5_get_read_count(file, &read_count_returned)); + REQUIRE(read_count_returned == read_count); + + pod5_close_and_free_reader(file); + CHECK_POD5_OK(pod5_get_error_no()); } } @@ -442,7 +633,7 @@ SCENARIO("C API Run Info") auto file = pod5_create_file(filename, "c_software", NULL); REQUIRE(file); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); std::vector context_tags_keys{"thing", "foo"}; std::vector context_tags_values{"thing_val", "foo_val"}; @@ -451,60 +642,58 @@ SCENARIO("C API Run Info") for (std::size_t i = 0; i < 10; ++i) { std::int16_t run_info_id = -1; - CHECK( - pod5_add_run_info( - &run_info_id, - file, - expected_acq_id(i).c_str(), - 15400, - adc_max, - adc_min, - context_tags_keys.size(), - context_tags_keys.data(), - context_tags_values.data(), - "experiment_name", - "flow_cell_id", - "flow_cell_product_code", - "protocol_name", - "protocol_run_id", - 200000, - "sample_id", - 4000, - "sequencing_kit", - "sequencer_position", - "sequencer_position_type", - "software", - "system_name", - "system_type", - tracking_id_keys.size(), - tracking_id_keys.data(), - tracking_id_values.data()) - == POD5_OK); + CHECK_POD5_OK(pod5_add_run_info( + &run_info_id, + file, + expected_acq_id(i).c_str(), + 15400, + adc_max, + adc_min, + context_tags_keys.size(), + context_tags_keys.data(), + context_tags_values.data(), + "experiment_name", + "flow_cell_id", + "flow_cell_product_code", + "protocol_name", + "protocol_run_id", + 200000, + "sample_id", + 4000, + "sequencing_kit", + "sequencer_position", + "sequencer_position_type", + "software", + "system_name", + "system_type", + tracking_id_keys.size(), + tracking_id_keys.data(), + tracking_id_values.data())); CHECK(run_info_id == i); } - CHECK(pod5_close_and_free_writer(file) == POD5_OK); + CHECK_POD5_OK(pod5_close_and_free_writer(file)); } // Read the file back: { - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); CHECK(!pod5_open_file(NULL)); auto file = pod5_open_file(filename); - CHECK(pod5_get_error_no() == POD5_OK); + CHECK_POD5_OK(pod5_get_error_no()); CHECK(pod5_get_error_string() == std::string{""}); CHECK(!!file); run_info_index_t run_info_count = 0; - CHECK(pod5_get_file_run_info_count(file, &run_info_count) == POD5_OK); + CHECK_POD5_OK(pod5_get_file_run_info_count(file, &run_info_count)); REQUIRE(run_info_count == 10); for (run_info_index_t i = 0; i < 10; ++i) { RunInfoDictData * run_info_data_out = nullptr; - CHECK(pod5_get_file_run_info(file, i, &run_info_data_out) == POD5_OK); + CHECK_POD5_OK(pod5_get_file_run_info(file, i, &run_info_data_out)); CHECK(run_info_data_out->acquisition_id == expected_acq_id(i)); pod5_free_run_info(run_info_data_out); } - CHECK(pod5_close_and_free_reader(file) == POD5_OK); + CHECK_POD5_OK(pod5_close_and_free_reader(file)); } } diff --git a/docs/requirements.txt b/docs/requirements.txt index 60d04bb..2e88468 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,4 +2,4 @@ sphinx-rtd-theme sphinx==v5.3.0 myst-parser # Paths are relative to project root for ReadTheDocs and docs/Makefile -pod5==0.2.0 +pod5==0.2.2 diff --git a/python/pod5/pyproject.toml b/python/pod5/pyproject.toml index 54d8b05..167c382 100644 --- a/python/pod5/pyproject.toml +++ b/python/pod5/pyproject.toml @@ -24,7 +24,7 @@ classifiers=[ ] dependencies = [ - "lib_pod5 == 0.2.0", + "lib_pod5 == 0.2.2", "iso8601", 'importlib-metadata; python_version<"3.8"', "more_itertools",