Skip to content

Commit

Permalink
Merge pull request #68 from eseiler/misc/coverage4
Browse files Browse the repository at this point in the history
[TEST] Increase codecoverage
  • Loading branch information
eseiler authored Aug 24, 2021
2 parents 5e05297 + b7b514b commit 23a675f
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 111 deletions.
3 changes: 3 additions & 0 deletions include/raptor/argument_parsing/shared.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace raptor

void init_shared_meta(seqan3::argument_parser & parser);
void try_parsing(seqan3::argument_parser & parser);
void parse_bin_paths(std::filesystem::path const & bin_file,
std::vector<std::vector<std::string>> & bin_paths,
bool const is_socks);

template <typename arguments_t>
void init_shared_options(seqan3::argument_parser & parser, arguments_t & arguments)
Expand Down
78 changes: 25 additions & 53 deletions include/raptor/argument_parsing/validators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class size_validator
class bin_validator
{
public:
using option_value_type = std::vector<std::filesystem::path>;
using option_value_type = std::vector<std::vector<std::string>>;

bin_validator() = default;
bin_validator(bin_validator const &) = default;
Expand All @@ -109,71 +109,43 @@ class bin_validator
bin_validator & operator=(bin_validator &&) = default;
~bin_validator() = default;

void operator() (std::vector<std::string> const & values) const
{
auto view = values | std::views::transform([] (auto const & str) { return std::filesystem::path{str}; });
operator()(view);
}

template <typename rng_t>
requires std::same_as<std::filesystem::path, std::remove_cvref_t<std::ranges::range_value_t<rng_t>>>
void operator() (rng_t const & values) const
void operator() (option_value_type const & values) const
{
if (values.empty())
throw seqan3::validation_error{"The list of input files cannot be empty."};

for (auto && value : values)
bool const is_minimiser_input = std::filesystem::path{values[0][0]}.extension() == ".minimiser";

for (std::vector<std::string> const & vector_of_paths : values)
{
try
for (std::string const & value : vector_of_paths)
{
sequence_file_validator(value);
}
catch (seqan3::validation_error const & exception)
{
if (value.extension() == ".minimiser")
minimiser_file_validator(value);
else if (values.size() == 1u)
{
std::ifstream list_of_files{value};
std::string line;
while (std::getline(list_of_files, line))
{
if (!line.empty())
{
std::filesystem::path bin_path{line};
if (bin_path.extension() == ".minimiser")
minimiser_file_validator(bin_path);
else
sequence_file_validator(bin_path);
}
}
}
else
throw exception;
}
}
std::filesystem::path const file_path{value};

bool const is_minimiser_input = values[0].extension() == ".minimiser";
if (is_minimiser_input && (file_path.extension() != ".minimiser"))
throw seqan3::validation_error{"You cannot mix sequence and minimiser files as input."};
if (std::filesystem::file_size(file_path) == 0u)
throw seqan3::validation_error{"The file " + value + " is empty."};

for (auto && value : values)
{
if (is_minimiser_input != (value.extension() == ".minimiser"))
throw seqan3::validation_error{"You cannot mix sequence and minimiser files as input."};
if (std::filesystem::file_size(value) == 0u)
throw seqan3::validation_error{"The file " + value.string() + " is empty."};
if (is_minimiser_input)
minimiser_file_validator(file_path);
else
sequence_file_validator(file_path);
}
}
}

std::string get_help_page_message() const
{
return seqan3::detail::to_string("The input file must exist and read permissions must be granted. Valid file "
"extensions for bin files are: [minimiser], or ", sequence_extensions,
// Update
return seqan3::detail::to_string("The file must contain at least one file path per line, with multiple paths "
"being separated by a whitespace. Each line in the file corresponds to one "
"bin. Valid extensions for the paths in the file are [minimiser] when "
" preprocessing, and ", sequence_extensions,
#if defined(SEQAN3_HAS_BZIP2) || defined(SEQAN3_HAS_ZLIB)
" possibly followed by: ", compression_extensions, ". ",
#else
". ",
", possibly followed by ", compression_extensions,
#endif
"All other extensions will be assumed to contain one line per path to a bin.");
" otherwise. ");
}

private:
Expand All @@ -189,11 +161,11 @@ class bin_validator
result.push_back("bgzf");
#endif
return result;
}()};
}()}; // LCOV_EXCL_LINE
std::vector<std::string> combined_extensions{[&] ()
{
if (compression_extensions.empty())
return sequence_extensions;
return sequence_extensions; // LCOV_EXCL_LINE
std::vector<std::string> result;
for (auto && sequence_extension : sequence_extensions)
{
Expand Down
49 changes: 3 additions & 46 deletions src/argument_parsing/build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ void init_build_parser(seqan3::argument_parser & parser, build_arguments & argum
"raptor build --kmer 19 --window 23 --compute-minimiser --output precomputed_minimisers all_bin_paths.txt",
"raptor build --size 8m --output minimiser_raptor.index all_minimiser_paths.txt"};
parser.add_positional_option(arguments.bin_file,
arguments.is_socks ? "File containing color and file names." :
"File containing one file per line per bin.",
(arguments.is_socks ? "File containing color and file names. " :
"File containing file names. ") + bin_validator{}.get_help_page_message(),
seqan3::input_file_validator{});
parser.add_option(arguments.parts,
'\0',
Expand Down Expand Up @@ -145,50 +145,7 @@ void run_build(seqan3::argument_parser & parser, bool const is_socks)
// ==========================================
// Process bin_path
// ==========================================
if (!arguments.is_socks) // File containing bin paths
{
std::ifstream istrm{arguments.bin_file};
std::string line;
bin_validator validator{};

while (std::getline(istrm, line))
{
if (!line.empty())
{
arguments.bin_path.emplace_back(std::vector<std::string>{line});
validator(arguments.bin_path.back());
}
}
}
else
{
std::ifstream istrm{arguments.bin_file};
std::string line;
std::string color_name;
std::string file_name;
std::vector<std::string> tmp;
bin_validator validator{};

while (std::getline(istrm, line))
{
if (!line.empty())
{
tmp.clear();
std::stringstream sstream{line};
sstream >> color_name;
while (std::getline(sstream, file_name, ' '))
{
if (!file_name.empty())
{
tmp.emplace_back(file_name);
}
}
validator(tmp);
arguments.bin_path.emplace_back(tmp);
}
}
}

parse_bin_paths(arguments.bin_file, arguments.bin_path, arguments.is_socks);
arguments.bins = arguments.bin_path.size();

// ==========================================
Expand Down
31 changes: 31 additions & 0 deletions src/argument_parsing/shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,35 @@ void try_parsing(seqan3::argument_parser & parser)
}
}

void parse_bin_paths(std::filesystem::path const & bin_file,
std::vector<std::vector<std::string>> & bin_paths,
bool const is_socks)
{
std::ifstream istrm{bin_file};
std::string line{};
std::string color_name{};
std::string file_name{};
std::vector<std::string> tmp{};

while (std::getline(istrm, line))
{
if (!line.empty())
{
tmp.clear();
std::stringstream sstream{line};

if (is_socks)
sstream >> color_name;

while (std::getline(sstream, file_name, ' '))
if (!file_name.empty())
tmp.emplace_back(file_name);

bin_paths.emplace_back(tmp);
}
}

bin_validator{}(bin_paths);
}

} // namespace raptor
13 changes: 1 addition & 12 deletions src/argument_parsing/upgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,7 @@ void run_upgrade(seqan3::argument_parser & parser)
// ==========================================
// Process bin_path
// ==========================================
std::ifstream istrm{arguments.bin_file};
std::string line;
auto sequence_file_validator{bin_validator{}.sequence_file_validator};

while (std::getline(istrm, line))
{
if (!line.empty())
{
sequence_file_validator(line);
arguments.bin_path.emplace_back(std::vector<std::string>{line});
}
}
parse_bin_paths(arguments.bin_file, arguments.bin_path, false);

// ==========================================
// Dispatch
Expand Down
70 changes: 70 additions & 0 deletions test/cli/raptor_options_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,76 @@ TEST_F(raptor_build, kmer_shape)
EXPECT_EQ(result.err, std::string{"[Error] You cannot set both shape and k-mer arguments.\n"});
}

TEST_F(raptor_build, zero_threads)
{
cli_test_result const result = execute_app("raptor", "build",
"--kmer 20",
"--threads 0",
"--size 8m",
"--output index.raptor",
tmp_bin_list_file.file_path);
EXPECT_NE(result.exit_code, 0);
EXPECT_EQ(result.out, std::string{});
EXPECT_EQ(result.err, std::string{"[Error] Validation failed for option --threads: The value must be a positive integer.\n"});
}

TEST_F(raptor_build, no_bins_in_file)
{
seqan3::test::create_temporary_snippet_file tmp_bin_list_empty{"empty.txt", std::string{"\n"}};

cli_test_result const result = execute_app("raptor", "build",
"--kmer 20",
"--size 8m",
"--output index.raptor",
tmp_bin_list_empty.file_path);
EXPECT_NE(result.exit_code, 0);
EXPECT_EQ(result.out, std::string{});
EXPECT_EQ(result.err, std::string{"[Error] The list of input files cannot be empty.\n"});
}

TEST_F(raptor_build, empty_file_in_bin)
{
seqan3::test::create_temporary_snippet_file empty_sequence_file{"empty.fasta", std::string{"\n"}};
seqan3::test::create_temporary_snippet_file tmp_empty_bin_file{"empty_bin.txt", std::string{"\n"} + empty_sequence_file.file_path.string()};

cli_test_result const result = execute_app("raptor", "build",
"--kmer 20",
"--size 8m",
"--output index.raptor",
tmp_empty_bin_file.file_path);
EXPECT_NE(result.exit_code, 0);
EXPECT_EQ(result.out, std::string{});
EXPECT_EQ(result.err, std::string{"[Error] The file " + tmp_empty_bin_file.file_path.parent_path().string() + "/empty.fasta is empty.\n"});
}

TEST_F(raptor_build, mixed_input)
{
seqan3::test::create_temporary_snippet_file minimiser_file{"bin1.minimiser", std::string{"\n0"}};
seqan3::test::create_temporary_snippet_file mixed_bin_file{"mixed.txt", std::string{"\n"} + minimiser_file.file_path.string() + std::string{"\nbin2.fasta"}};

cli_test_result const result = execute_app("raptor", "build",
"--kmer 20",
"--size 8m",
"--output index.raptor",
mixed_bin_file.file_path);
EXPECT_NE(result.exit_code, 0);
EXPECT_EQ(result.out, std::string{});
EXPECT_EQ(result.err, std::string{"[Error] You cannot mix sequence and minimiser files as input.\n"});
}

TEST_F(raptor_build, wrong_parts)
{
cli_test_result const result = execute_app("raptor", "build",
"--kmer 20",
"--size 8m",
"--parts 3",
"--output index.raptor",
tmp_bin_list_file.file_path);
EXPECT_NE(result.exit_code, 0);
EXPECT_EQ(result.out, std::string{});
EXPECT_EQ(result.err, std::string{"[Error] Validation failed for option --parts: The value must be a power of two.\n"});
}

TEST_F(raptor_search, ibf_missing)
{
cli_test_result const result = execute_app("raptor", "search",
Expand Down

0 comments on commit 23a675f

Please sign in to comment.