fn new(
        seed: Seed,
        index1: usize,
        index2: usize,
        haystack_len: usize,
        needle_len: usize,
        fwd: Option<usize>,
    ) -> Option<Test> {
        let mut index1: u8 = index1.try_into().unwrap();
        let mut index2: u8 = index2.try_into().unwrap();
        // The '#' byte is never used in a haystack (unless we're expecting
        // a match), while the '@' byte is never used in a needle.
        let mut haystack = vec![b'@'; haystack_len];
        let mut needle = vec![b'#'; needle_len];
        needle[0] = seed.first;
        needle[index1 as usize] = seed.index1;
        needle[index2 as usize] = seed.index2;
        // If we're expecting a match, then make sure the needle occurs
        // in the haystack at the expected position.
        if let Some(i) = fwd {
            haystack[i..i + needle.len()].copy_from_slice(&needle);
        }
        // If the operations above lead to rare offsets pointing to the
        // non-first occurrence of a byte, then adjust it. This might lead
        // to redundant tests, but it's simpler than trying to change the
        // generation process I think.
        if let Some(i) = crate::memchr(seed.index1, &needle) {
            index1 = u8::try_from(i).unwrap();
        }
        if let Some(i) = crate::memchr(seed.index2, &needle) {
            index2 = u8::try_from(i).unwrap();
        }
        Some(Test { haystack, needle, index1, index2, fwd })
    }
Create a new "packed pair" test from a seed and some given offsets to
the pair of bytes to use as a predicate in the seed's needle.

If a valid test could not be constructed, then None is returned.
(Currently, we take the approach of massaging tests to be valid
instead of rejecting them outright.)
src/tests/packedpair.rs