```rust
mod tests {
    use super::*;
    use tempfile::tempdir;
    use test_case::test_case;

    #[test_case("xyzXYZ", "xyz",
    SearchOptions::default(),
    vec![0..3]; "case_sensitive_exact_match")]
    #[test_case("xyzXYZ", "xyz",
    SearchOptions { case_insensitive: true, ..Default::default() },
    vec![0..3, 3..6]; "case_insensitive_match")]
    #[test_case("xyzXYZ", "xyz",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![]; "whole_word_no_match")]
    #[test_case("abc abc1 abc", "abc",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![0..3, 9..12]; "whole_word_multiple_matches")]
    #[test_case("Apple apple XYZ", "aPpLe",
    SearchOptions { case_insensitive: true, whole_word: true, ..Default::default() },
    vec![0..5, 6..11]; "case_insensitive_whole_word")]
    #[test_case("abc,abcabc", "abc",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![0..3]; "whole_word_with_punctuation")]
    #[test_case("ababab", "aba",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![]; "no_overlapping_whole_word")]
    #[test_case("test this is a test", "test",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![0..4, 15..19]; "whole_word_at_end")]
    #[test_case("abc_abc $abc", "abc",
    SearchOptions { whole_word: true, ..Default::default() },
    vec![9..12]; "whole_word_with_special_characters")]
    fn test_find_cols(
        text: &str,
        pattern: &str,
        options: SearchOptions,
        result: Vec<Range<usize>>,
    ) {
        assert_eq!(find_match_range(text, pattern, &options), result);
    }

    const LINE1: &str = "The quick brown fox jumps over the lazy dog.";
    const LINE2: &str = "The quick brown fox is very quick.";
    const LINE3: &str = "A quick brown fox is a quick animal.";

    fn new_hash_map(ls: Vec<(usize, &str)>) -> HashMap<usize, String> {
        let mut map = HashMap::new();
        map.extend(
            ls.iter()
                .map(|(i, s)| (*i, s.to_string()))
                .collect::<Vec<_>>(),
        );
        map
    }

    #[test_case(&format!("{LINE1}\n{LINE2}\n{LINE3}"), "is very",
    (new_hash_map(vec![(2, LINE2)]),
    vec![MatchPosition {row: 2, char_range: 20..27}]),
    SearchOptions::default();
    "single")]
    #[test_case(&format!("{LINE1}\n{LINE2}\n{LINE3}"), "quick",
    (new_hash_map(vec![(1, LINE1), (2, LINE2), (3, LINE3)]),
    vec![MatchPosition {row: 1, char_range: 4..9}, MatchPosition {row: 2, char_range: 4..9}, MatchPosition {row: 2, char_range: 28..33}, MatchPosition {row: 3, char_range: 2..7}, MatchPosition {row: 3, char_range: 23..28}]),
    SearchOptions::default();
    "multiple same line")]
    #[test_case("line 1
line 2
line 3
line 4
line 5
line 6", "line 4", (new_hash_map(vec![(3, "line 3"), (4, "line 4"), (5, "line 5")]),
    vec![MatchPosition {row: 4, char_range: 0..6}]),
    SearchOptions {line_buffer: 1, ..Default::default()};
    "line buffer"
    )]
    #[test_case("line 1
line 2
line 3
line 4
line 5
line 6", "line 2", (new_hash_map(vec![(1, "line 1"), (2, "line 2"), (3, "line 3"), (4, "line 4")]),
    vec![MatchPosition {row: 2, char_range: 0..6}]),
    SearchOptions {line_buffer: 2, ..Default::default()};
    "line buffer clipped at start"
    )]
    #[test_case("line 1
line 2
line 3
line 4
line 5
line 6", "line 6", (new_hash_map(vec![(4, "line 4"), (5, "line 5"), (6, "line 6")]),
    vec![MatchPosition {row: 6, char_range: 0..6}]),
    SearchOptions {line_buffer: 2, ..Default::default()};
    "line buffer clipped at end"
    )]
    #[test_case("line 1
line 2
line a
line 4
line a
line 6", "line a", (new_hash_map(vec![(2, "line 2"), (3, "line a"), (4, "line 4"), (5, "line a"), (6, "line 6")]),
    vec![MatchPosition {row: 3, char_range: 0..6}, MatchPosition {row: 5, char_range: 0..6}]),
    SearchOptions {line_buffer: 1, ..Default::default()};
    "line buffer overlap"
    )]
    fn test_search(file: &str, pattern: &str, result: FileMatchData, options: SearchOptions) {
        let temp = tempdir().unwrap();
        std::fs::write(temp.path().join("file"), file).unwrap();
        assert_eq!(
            search(temp.path(), pattern, &options).first().unwrap().1,
            result
        )
    }
}
```
refactor and format