Discovering the current test (name)

What are we contemplating here? Does the #[test] attribute have a stable interface that must be obeyed, or can we add to it in some way? If we can add to it, I'd love to see a trait defined that tests can implement that allows forward-compatible extension of tests. Purely as a strawman:

// Configurations

#[derive(Debug, Default)]
pub struct TestConfigurationVersion2 {
    test_name: String,
    // Whatever else is being contemplated for this version
}

#[derive(Debug)]
#[non_exhaustive]
pub enum TestConfiguration {
    Version1,
    Version2(TestConfigurationVersion2),
}

// Errors

#[derive(Debug)]
pub enum TestConfigurationErrorVersion2 {
    // Indicates that the test cannot handle the given version of
    // the test framework's incoming configuration.  Ideally, we'll
    // add some information here that the test framework can use
    // to try and redo the test using an earlier version of the
    // configuration, but it's entirely possible to simple try
    // all supported variants of `TestConfiguration` until one of
    // them works, and to report the test as a failure if that
    // doesn't happen.
    VersionError,

    Failed(), // Something that derives std::error::Error

    /// If this is returned, then the test passed, BUT the test has a
    /// suggestion for another test to try.  This makes it possible to
    /// build intelligent fuzzers that use the results of prior tests
    /// to narrow down what caused the test to pass.
    PassedTestNext(TestConfigurationVersion2),

    /// If this is returned, then the test failed, BUT the test has a
    /// suggestion for another test to try.  This makes it possible to
    /// build intelligent fuzzers that use the results of prior tests
    /// to narrow down what caused the test to pass.
    FailedTestNext(TestConfigurationVersion2),
}

#[derive(Debug)]
#[non_exhaustive]
pub enum TestConfigurationError {
    Version1,
    Version2(TestConfigurationErrorVersion2),
}

// The test trait

pub trait TestTrait {
    #[allow(unused_variables)]
    fn constructor(
        config: TestConfiguration,
    ) -> Result<Self, TestConfigurationError>
    where
        Self: Sized,
        Self: Default,
    {
        match config {
            TestConfiguration::Version1 => Ok(Default::default()),
            TestConfiguration::Version2(..) => {
                Err(TestConfigurationError::Version2(
                    TestConfigurationErrorVersion2::VersionError,
                ))
            },
        }
    }

    fn test(self) -> Result<(), TestConfigurationError>
    where
        Self: Sized;
}

The idea is that #[test] would become more like a derive macro, converting all current tests into objects that implements TestTrait, but which have a do-nothing constructor and a test method that is compatible with the current test methods (there will be some fiddling to get this right, I banged the code out pretty fast without any real testing).

The main idea is that the interface should be forward-compatible. Although the configurations for any given version of libtest are fixed, we can add in as many versions as we wish via new variants to TestConfiguration. Similar statements can be made for TestConfigurationError.

In addition, a test is able to return a new test configuration that can be used for a different iteration of the test. That could be useful for feedback-driven fuzzing tests where (once you've minimized the configuration causing the issues), you want to save the configuration in a regression database.

There are a whole host of other issues to solve here, like whether or not async tests would be a good idea, etc., but this may be a ways forward that doesn't break older tests.