-
Notifications
You must be signed in to change notification settings - Fork 26
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
Remove test-api-feature from chain-time #376
Remove test-api-feature from chain-time #376
Conversation
chain-test-utils/src/time/mod.rs
Outdated
gen().into() | ||
} | ||
|
||
// Generate an `Epoch` given a `smoke::R` (random generator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we now transitioning to smoke
from quickcheck
, which also has a framework for arbitrary, but reproducible stuff? Does it add value to use both?
I wish I was able to see what smoke
can do from the documentation, but there's scarcely any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the thing with quickcheck::Arbitrary
is that since it is a trait
it should be implemented in the same crate as the target type. So, the idea here is to have a "centralize" crate with the utilities to generate our inner types. That way that crate can be simply used in the tests
without relying on features. We can still use quickcheck::Arbitrary
for the self crate tests since it is useful, but rely on chain-test-utils
for whatever is not in that crate.
About smoke, it just has some trait for generating types and abstracts some of the most basic generation of numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, the idea here is to have a "centralize" crate with the utilities to generate our inner types.
That might prove a bit more difficult to maintain in separation from the crates defining the types.
That way that crate can be simply used in the tests without relying on features.
What was the original problem we are trying to solve here? The "property-test-api" feature added the testing trait impls and possibly other test utilities for testing uses, and it was to be used in approximately this way:
# For non-test binaries.
# If the whole target is meant for testing, add "property-test-api" here as well
[dependencies]
chain-core = { path = "chain-deps/chain-core" }
# For cfg(test), integration tests under tests/, etc.
[dev-dependencies]
chain-core = { path = "chain-deps/chain-core", features = "property-test-api" }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mzabaluev the whole point of a generator based approach, is that it allow to define those generator elsewhere, since they are effectively just function/closures. A generator is just an "infinite" iterator of random value where the random engine is deterministically generated from a given seed, hence assuring reproducibility from a given Seed
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm not explaining it properly, sorry.
With the property-test-api
we are exporting the quickcheck
dep. We are using this feature in the jormungandr tests (can't point out where exactly now). Which apparently is a misuse.
I see your point though. We could keep the feature for testing just the chain-deps
. (But we may have the temptation in the future to use it just because it may be easier 🍔 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are two things:
- we want a more flexible API than
arbitrary
to construct testing data; - there is a concern about test facilities embedded into the library crates and conditionally enabled, as being cumbersome to use, or easy to get wrong and pull in unnecessary dependencies.
Would the latter be a matter of convention that has been followed only loosely, which may be what is causing problems?
Another benefit of testing APIs built into crates is that you don't have to break encapsulation to provide the testing facilities: a public generator defined in a conditionally compiled testing
submodule can freely use the type's internals to generate values, while we don't want to provide equivalent freedom to construct values in public API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another benefit of testing APIs built into crates is that you don't have to break encapsulation to provide the testing facilities: a public generator defined in a conditionally compiled testing submodule can freely use the type's internals to generate values, while we don't want to provide equivalent freedom to construct values in public API.
Yes, this is something that will happen for sure.
Well, we can keep both though. And careful use them where they are needed.
Fix time era test (missing including end value)
I'm letting the |
chain-test-utils/src/time/mod.rs
Outdated
|
||
// `TimeEra` configuration, encapsulates the building boundaries for the inner data | ||
#[derive(Clone)] | ||
pub struct TimeEraGenCfg { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a bit less abbreviated: TimeEraGenConfig
chain-test-utils/src/time/mod.rs
Outdated
pub fn new(config: Option<TimeEraGenCfg>) -> Self { | ||
Self { config } | ||
} | ||
|
||
pub fn with_config(&mut self, config: TimeEraGenCfg) { | ||
self.config = Some(config); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look a bit redundant: new
could use the default configuration, so just be the same as the Default
impl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TimeEraGenCfg
struct looks like it could provide a builder-style API, but maybe it's too much polish for the testing interface.
self.config = Some(config); | ||
} | ||
|
||
pub fn clear_config(&mut self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need the mutability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhm, for modifying the struct I think we need.
chain-test-utils/src/time/mod.rs
Outdated
} | ||
|
||
// Generate an `TimeEra` given a `smoke::R` (random generator) and a `TimeEraGenCfg` range limit tuple | ||
pub fn generate_time_era_with_config(r: &mut R, config: &TimeEraGenCfg) -> TimeEra { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This (as a public API) also looks redundant to the composition of TimeEraGenerator::with_config
and the gen
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, my idea was that some times you will need a one shot time build and some times you will need a bunch of generated items. It feels overkill to build a generator just to get one object when you can just call a method.
pub fn generate_slot(r: &mut R) -> Slot { | ||
generate_slot_with(|| r.num()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this generate nonsensical slot values?
I'm trying to understand the purpose of non-bounded random generator methods here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it actually just makes sense if you want to test both acceptable values and not.
chain-test-utils/src/time/mod.rs
Outdated
|
||
// Generator wrapper for TimeEra generator methods | ||
pub struct TimeEraGenerator { | ||
config: Option<TimeEraGenCfg>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have a non-optional config and set range bounds to sensible defaults for a TimeEra
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where can I find some documentation about what would be some range bounds for a TimeEra
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. @vincenthz ?
I would like the elementary newtype values to be domain-valid by construction, unless their domain really covers the entire underlying integer type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some documentation to the types and the functions. I am looking at what kind of type can be added and how they are suppose to be used/interact with each other.
Also ergonimically speaking, put the types at the top if possible. So we know what we are looking for and the impl
at the bottom (before the tests mod though).
chain-test-utils/src/time/mod.rs
Outdated
pub fn new(config: Option<TimeEraGenCfg>) -> Self { | ||
Self { config } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the point to have a new
with an optional of something and then a with_config
to set the config?
use a Config
and a ConfigBuilder
if you need it. Use Default
too. This will make your types more ergonomic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I was thinking that we may want to change the way the generator generates values mid-way on some computation and still keep the reproducible step from the same seed. I see that with_config
was an unfortunate name for this. It should be a set_config
.
Refactor TimeEraGenerator constructors
@NicolasDP , I expanded (and fixed) the documentation. But I'm not sure if it was in the way you intended. I think I did not understand properly what you meant about the types interaction. |
I'm closing this for now. It will be resumed in the future. |
Another module with support in the new
chain-test-utils
.