Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mocks for some builtin types for use when Godot isn't running #701

Open
lilizoey opened this issue May 5, 2024 · 5 comments
Open

Add mocks for some builtin types for use when Godot isn't running #701

lilizoey opened this issue May 5, 2024 · 5 comments
Labels
c: tooling CI, automation, tools quality-of-life No new functionality, but improves ergonomics/internals

Comments

@lilizoey
Copy link
Member

lilizoey commented May 5, 2024

In some cases it would be nice to be able to use at least some basic features of some builtin types even when Godot isn't running.

For instance, in doctests we cannot use GString or StringName because they only work when Godot is running. However these are "just" strings. It would be nice if we could make doctests and unit tests for types and functions that use these types anyway.

This should only be used in cases where the exact values and behavior of these types is not what's being tested beyond some basic features, such as equality.

We could override some of the ffi-functions with stubs/mocks when Godot isn't running. And maybe we should make it an explicit opt-in, like for instance we could have a global function pub unsafe fn enable_builtin_mocks() function to enable this. This should be unsafe imo because otherwise we may need to reimplement a lot of functionality for relevant types as we may rely on some behavior for safety.

@lilizoey lilizoey added quality-of-life No new functionality, but improves ergonomics/internals c: tooling CI, automation, tools labels May 5, 2024
@Bromeon
Copy link
Member

Bromeon commented May 5, 2024

I tried something like this in the early days, until I discovered how broken #[cfg(test)] and #[cfg(doctest)] truly are.

This is a particularly funny commit: 698e21b
And here's one of many attempts to rectify the situation: 48b7c6e
Here's another: 7ef9c75

I tried a lot of things, but it's very hard to satisfy test, doctest, build and clippy targets simultaneously without resorting to dedicated features or custom --cfg flags. And doing so makes it no longer possible to run cargo test, to the point the library looks broken when a user tests it. Eventually I had enough and wiped the whole thing 😬

So, we can gladly try another angle, but:

  • For isolated doctests, it's possible to just declare stubs locally. We do this in a few places where the whole symbol would be too heavy.
  • Mocking the types somewhat defeats the purpose of doctests which test the actual API. If we change the Godot API but forget to update the mocks, the tests become outdated. On the contrary, ```no_run at least compiles against the right API.
  • I'm not willing to reintroduce the former #[cfg] hell that was very invasive and tedious to get right. We already have a lot of conditional compilation to support different API versions and features, this adds just another dimension.
  • We will get extra maintenance effort for stubs, to keep them in sync, keep up with new clippy lints, etc.

So maybe there's a very lightweight way to do this, but imo it's not worth the cost of massive reconfiguration across crates. We need to ask ourselves what the goal would be -- if we want to test the integration, then stubs/mocks don't provide that, and we already have itests for this purpose. If we want to demonstrate features, we can currently do that with ```no_run -- sure, that doesn't run the actual Godot code, but neither does a stub/mock.

@lilizoey
Copy link
Member Author

lilizoey commented May 5, 2024

well for instance if i want to test some helper functions implemented on PropertyInfo then i must make it an integration test. since in order to create a PropertyInfo i must create a GString StringName and ClassName even if none of the functionality im testing actually cares about those types at all beyond very basic things.

im not suggesting we're going to be testing GString through mocks but rather things like PropertyInfo

@lilizoey
Copy link
Member Author

lilizoey commented May 5, 2024

Here is a concrete example from me working on get_property_list. i wanted to add some helper functions to make PropertyInfo easier to construct and work with so i added this:

/// Change the `hint` and `hint_string` to be the given `hint_info`.
///
/// See [`export_info_functions`](crate::property::export_info_function) for functions that return appropriate `PropertyHintInfo`s for
/// various Godot annotations.
///
/// # Examples
///
/// Creating an `@export_range` property.
///
// TODO: Make this nicer to use.
/// ```no_run
/// # use crate::property::export_info_function;
/// # use crate::builtin::meta::PropertyInfo;
///
/// let property = PropertyInfo::new_export::<f64>("my_range_property")
///     .with_hint_info(export_info_function::export_range(
///         0.0,
///         10.0,
///         Some(0.1),
///         false,
///         false,
///         false,
///         false,
///         false,
///         false,
///     ))
/// ```
pub fn with_hint_info(self, hint_info: PropertyHintInfo) -> Self {
    let PropertyHintInfo { hint, hint_string } = hint_info;

    Self {
        hint,
        hint_string,
        ..self
    }
}

This doctest does not actually care about any godot-specific details of GString, StringName, etc. all it really would care about is that i can construct them, and test for equality (if i add an assert).

@Bromeon
Copy link
Member

Bromeon commented May 5, 2024

I see, thanks for the example!

In this case, a mock could probably live in a dedicated module, whose import is hidden with #?

But how can we easily keep the mock and the real API in sync? Because if we need to duplicate code, we may as well just duplicate the doctest into an integration test...

@StatisMike
Copy link
Contributor

IMO the itest seems like a better place for that.

Less type-changes: the better. Doctest could check only for compile errors with no_run. To improve maintability comment with the information about itest existence could be added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: tooling CI, automation, tools quality-of-life No new functionality, but improves ergonomics/internals
Projects
None yet
Development

No branches or pull requests

3 participants