1use super::{
2 Alias, Architecture, BitWidth, Core, Device, DeviceType, Group, Job, ProcessorFamily, Tag,
3 TestCase, TestSet, TestSuite, User, Worker,
4};
5
6use boulder::{
7 Buildable, Builder, GeneratableWithPersianRug, GeneratorWithPersianRug,
8 GeneratorWithPersianRugIterator, GeneratorWithPersianRugMutIterator, RepeatFromPersianRug,
9 SubsetsFromPersianRug, TryRepeatFromPersianRug,
10};
11use clone_replace::{CloneReplace, MutateGuard};
12use django_query::mock::clone_replace::persian_rug::CloneReplacePersianRugTableSource;
13use django_query::mock::{EndpointWithContext, NestedEndpointParams, NestedEndpointWithContext};
14use persian_rug::{Context, Mutator, Proxy};
15use std::sync::Arc;
16
17#[derive(Clone, Debug, Default)]
22#[persian_rug::persian_rug]
23pub struct State {
24 #[table]
25 aliases: Alias<State>,
26 #[table]
27 architectures: Architecture<State>,
28 #[table]
29 bit_widths: BitWidth<State>,
30 #[table]
31 cores: Core<State>,
32 #[table]
33 devices: Device<State>,
34 #[table]
35 device_types: DeviceType<State>,
36 #[table]
37 groups: Group<State>,
38 #[table]
39 jobs: Job<State>,
40 #[table]
41 processor_family: ProcessorFamily<State>,
42 #[table]
43 tags: Tag<State>,
44 #[table]
45 test_cases: TestCase<State>,
46 #[table]
47 test_sets: TestSet<State>,
48 #[table]
49 test_suites: TestSuite<State>,
50 #[table]
51 users: User<State>,
52 #[table]
53 workers: Worker<State>,
54}
55
56pub struct SharedState(CloneReplace<State>);
72
73impl SharedState {
74 pub fn new() -> Self {
83 Self(CloneReplace::new(State::new()))
84 }
85
86 pub fn new_populated(pop: PopulationParams) -> Self {
98 Self(CloneReplace::new(State::new_populated(pop)))
99 }
100
101 pub fn endpoint<T>(
124 &self,
125 uri: Option<&str>,
126 default_limit: Option<usize>,
127 ) -> EndpointWithContext<
128 CloneReplacePersianRugTableSource<
129 impl Fn(&Arc<State>) -> persian_rug::TableIterator<'_, T> + Clone,
130 State,
131 >,
132 >
133 where
134 T: persian_rug::Contextual<Context = State> + 'static,
135 State: persian_rug::Owner<T>,
136 {
137 let mut ep = EndpointWithContext::new(
138 CloneReplacePersianRugTableSource::new(
139 self.0.clone(),
140 |s: &Arc<State>| -> persian_rug::TableIterator<'_, T> { s.get_iter() },
141 ),
142 uri,
143 );
144 if let Some(default_limit) = default_limit {
145 ep.default_limit(default_limit);
146 }
147 ep
148 }
149
150 pub fn nested_endpoint<T>(
189 &self,
190 params: NestedEndpointParams<'_>,
191 default_limit: Option<usize>,
192 ) -> NestedEndpointWithContext<
193 CloneReplacePersianRugTableSource<
194 impl Fn(&Arc<State>) -> persian_rug::TableIterator<'_, T> + Clone,
195 State,
196 >,
197 >
198 where
199 T: persian_rug::Contextual<Context = State> + 'static,
200 State: persian_rug::Owner<T>,
201 {
202 let mut ep = NestedEndpointWithContext::new(
203 CloneReplacePersianRugTableSource::new(
204 self.0.clone(),
205 |s: &Arc<State>| -> persian_rug::TableIterator<'_, T> { s.get_iter() },
206 ),
207 params,
208 );
209 if let Some(default_limit) = default_limit {
210 ep.default_limit(default_limit);
211 }
212 ep
213 }
214
215 pub fn access(&self) -> Arc<State> {
231 self.0.access()
232 }
233
234 pub fn mutate(&mut self) -> MutateGuard<State> {
249 self.0.mutate()
250 }
251}
252
253impl Clone for SharedState {
254 fn clone(&self) -> Self {
255 SharedState(self.0.clone())
256 }
257}
258
259impl Default for SharedState {
260 fn default() -> Self {
261 Self::new()
262 }
263}
264
265#[derive(Buildable, Clone, Debug, Eq, PartialEq)]
293pub struct PopulationParams {
294 #[boulder(default = 10usize)]
295 pub aliases: usize,
296 #[boulder(default = 5usize)]
297 pub architectures: usize,
298 #[boulder(default = 2usize)]
299 pub bit_widths: usize,
300 #[boulder(default = 3usize)]
301 pub cores: usize,
302 #[boulder(default = 50usize)]
303 pub devices: usize,
304 #[boulder(default = 10usize)]
305 pub device_types: usize,
306 #[boulder(default = 3usize)]
307 pub groups: usize,
308 #[boulder(default = 200usize)]
309 pub jobs: usize,
310 #[boulder(default = 3usize)]
311 pub processor_families: usize,
312 #[boulder(default = 5usize)]
313 pub tags: usize,
314 #[boulder(default = 5usize)]
315 pub test_cases: usize,
316 #[boulder(default = 2usize)]
317 pub test_sets: usize,
318 #[boulder(default = 3usize)]
319 pub test_suites: usize,
320 #[boulder(default = 5usize)]
321 pub users: usize,
322 #[boulder(default = 10usize)]
323 pub workers: usize,
324}
325
326impl PopulationParams {
327 pub fn new() -> Self {
339 Default::default()
340 }
341}
342
343impl Default for PopulationParams {
344 fn default() -> Self {
345 Self::builder().build()
346 }
347}
348
349struct JobGenerator {
350 job: Option<Proxy<Job<State>>>,
351}
352
353impl JobGenerator {
354 pub fn new(job: Option<Proxy<Job<State>>>) -> Self {
355 Self { job }
356 }
357}
358
359impl GeneratorWithPersianRug<State> for JobGenerator {
360 type Output = Proxy<Job<State>>;
361
362 fn generate<'b, B>(&mut self, context: B) -> (Self::Output, B)
363 where
364 B: 'b + Mutator<Context = State>,
365 {
366 (self.job.unwrap(), context)
367 }
368}
369
370struct SuiteGenerator {
371 suite: usize,
372 suites: Vec<Proxy<TestSuite<State>>>,
373}
374
375impl SuiteGenerator {
376 pub fn new(suites: Vec<Proxy<TestSuite<State>>>) -> Self {
377 SuiteGenerator { suite: 0, suites }
378 }
379}
380
381impl GeneratorWithPersianRug<State> for SuiteGenerator {
382 type Output = Proxy<TestSuite<State>>;
383
384 fn generate<'b, B>(&mut self, context: B) -> (Self::Output, B)
385 where
386 B: 'b + Mutator<Context = State>,
387 {
388 let suite = self.suites[self.suite];
389 self.suite = (self.suite + 1) % self.suites.len();
390
391 (suite, context)
392 }
393}
394
395struct SetGenerator {
396 suite: usize,
397 set: usize,
398 suites: Vec<Proxy<TestSuite<State>>>,
399 sets: Vec<Proxy<TestSet<State>>>,
400}
401
402impl SetGenerator {
403 fn new(suites: Vec<Proxy<TestSuite<State>>>, sets: Vec<Proxy<TestSet<State>>>) -> Self {
404 SetGenerator {
405 suite: 0,
406 set: 0,
407 suites,
408 sets,
409 }
410 }
411}
412
413impl GeneratorWithPersianRug<State> for SetGenerator {
414 type Output = Option<Proxy<TestSet<State>>>;
415
416 fn generate<'b, B>(&mut self, context: B) -> (Self::Output, B)
417 where
418 B: 'b + Mutator<Context = State>,
419 {
420 if self.suites.is_empty() || self.sets.is_empty() {
421 return (None, context);
422 }
423
424 let suite = self.suites[self.suite];
425 self.suite = (self.suite + 1) % self.suites.len();
426
427 let mut attempts = 0;
428 let set = loop {
429 let set = self.sets[self.set];
430 self.set = (self.set + 1) % self.sets.len();
431 attempts += 1;
432 if context.get(&set).suite == suite {
433 break Some(set);
434 }
435 if attempts == self.sets.len() {
436 break None;
437 }
438 };
439
440 (set, context)
441 }
442}
443
444impl State {
445 pub fn new() -> Self {
447 Default::default()
448 }
449
450 pub fn make_device_type_generator(
458 ) -> impl GeneratorWithPersianRug<State, Output = Proxy<DeviceType<State>>> {
459 Proxy::<DeviceType<State>>::generator()
460 .aliases(SubsetsFromPersianRug::new())
461 .architecture(TryRepeatFromPersianRug::new())
462 .bits(TryRepeatFromPersianRug::new())
463 .cores(SubsetsFromPersianRug::new())
464 .processor(TryRepeatFromPersianRug::new())
465 }
466
467 pub fn make_user_generator() -> impl GeneratorWithPersianRug<State, Output = Proxy<User<State>>>
474 {
475 Proxy::<User<State>>::generator().group(TryRepeatFromPersianRug::new())
476 }
477
478 pub fn make_device_generator(
486 ) -> impl GeneratorWithPersianRug<State, Output = Proxy<Device<State>>> {
487 Proxy::<Device<State>>::generator()
488 .device_type(RepeatFromPersianRug::new())
489 .physical_owner(TryRepeatFromPersianRug::new())
490 .physical_group(TryRepeatFromPersianRug::new())
491 .tags(SubsetsFromPersianRug::new())
492 .worker_host(RepeatFromPersianRug::new())
493 }
494
495 pub fn make_job_generator() -> impl GeneratorWithPersianRug<State, Output = Proxy<Job<State>>> {
503 Proxy::<Job<State>>::generator()
504 .submitter(RepeatFromPersianRug::new())
505 .viewing_groups(SubsetsFromPersianRug::new())
506 .requested_device_type(TryRepeatFromPersianRug::new())
507 .tags(SubsetsFromPersianRug::new())
508 .actual_device(TryRepeatFromPersianRug::new())
509 }
510
511 pub fn new_populated(pop: PopulationParams) -> Self {
532 let mut s: State = Default::default();
533
534 let aliases = Proxy::<Alias<State>>::generator();
535 let _ = GeneratorWithPersianRugIterator::new(aliases, &mut s)
536 .take(pop.aliases)
537 .collect::<Vec<_>>();
538
539 let architectures = Proxy::<Architecture<State>>::generator();
540 let _ = GeneratorWithPersianRugIterator::new(architectures, &mut s)
541 .take(pop.architectures)
542 .collect::<Vec<_>>();
543
544 let bit_widths = Proxy::<BitWidth<State>>::generator();
545 let _ = GeneratorWithPersianRugIterator::new(bit_widths, &mut s)
546 .take(pop.bit_widths)
547 .collect::<Vec<_>>();
548
549 let cores = Proxy::<Core<State>>::generator();
550 let _ = GeneratorWithPersianRugIterator::new(cores, &mut s)
551 .take(pop.cores)
552 .collect::<Vec<_>>();
553
554 let processor_families = Proxy::<ProcessorFamily<State>>::generator();
555 let _ = GeneratorWithPersianRugIterator::new(processor_families, &mut s)
556 .take(pop.processor_families)
557 .collect::<Vec<_>>();
558
559 let device_types = Self::make_device_type_generator();
560 let _ = GeneratorWithPersianRugIterator::new(device_types, &mut s)
561 .take(pop.device_types)
562 .collect::<Vec<_>>();
563
564 let groups = Proxy::<Group<State>>::generator();
565 let _ = GeneratorWithPersianRugIterator::new(groups, &mut s)
566 .take(pop.groups)
567 .collect::<Vec<_>>();
568
569 let users = Self::make_user_generator();
570 let _ = GeneratorWithPersianRugIterator::new(users, &mut s)
571 .take(pop.users)
572 .collect::<Vec<_>>();
573
574 let workers = Proxy::<Worker<State>>::generator();
575 let _ = GeneratorWithPersianRugIterator::new(workers, &mut s)
576 .take(pop.workers)
577 .collect::<Vec<_>>();
578
579 let tags = Proxy::<Tag<State>>::generator();
580 let _ = GeneratorWithPersianRugIterator::new(tags, &mut s)
581 .take(pop.tags)
582 .collect::<Vec<_>>();
583
584 let devices = Self::make_device_generator();
585 let _ = GeneratorWithPersianRugIterator::new(devices, &mut s)
586 .take(pop.devices)
587 .collect::<Vec<_>>();
588
589 let jobs = Self::make_job_generator();
590 let jobs = GeneratorWithPersianRugIterator::new(jobs, &mut s)
591 .take(pop.jobs)
592 .collect::<Vec<_>>();
593
594 let mut suites = Proxy::<TestSuite<State>>::generator().job(JobGenerator::new(None));
595 let mut sets = Proxy::<TestSet<State>>::generator().suite(SuiteGenerator::new(Vec::new()));
596 let mut cases = Proxy::<TestCase<State>>::generator()
597 .suite(SuiteGenerator::new(Vec::new()))
598 .test_set(SetGenerator::new(Vec::new(), Vec::new()));
599
600 for job in jobs {
601 suites = suites.job(JobGenerator::new(Some(job)));
602 let suites = GeneratorWithPersianRugMutIterator::new(&mut suites, &mut s)
603 .take(pop.test_suites)
604 .collect::<Vec<_>>();
605
606 sets = sets.suite(SuiteGenerator::new(suites.clone()));
607 let sets = GeneratorWithPersianRugMutIterator::new(&mut sets, &mut s)
608 .take(pop.test_sets)
609 .collect::<Vec<_>>();
610
611 cases = cases
612 .suite(SuiteGenerator::new(suites.clone()))
613 .test_set(SetGenerator::new(suites.clone(), sets.clone()));
614 let _ = GeneratorWithPersianRugMutIterator::new(&mut cases, &mut s)
615 .take(pop.test_cases)
616 .collect::<Vec<_>>();
617 }
618
619 s
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626 use crate::{JobState, SharedState};
627
628 use anyhow::Result;
629 use boulder::{BuildableWithPersianRug, BuilderWithPersianRug};
630 use persian_rug::Proxy;
631 use serde_json::{json, Value};
632
633 async fn make_request<T, U>(server_uri: T, endpoint: U) -> Result<Value>
634 where
635 T: AsRef<str>,
636 U: AsRef<str>,
637 {
638 let url = format!("{}/api/v0.2/{}", server_uri.as_ref(), endpoint.as_ref());
639 Ok(reqwest::get(&url).await?.json().await?)
640 }
641
642 #[tokio::test]
643 async fn test_state() {
644 let mut p = SharedState::new();
645 {
646 let m = p.mutate();
647 let (_, m) = Proxy::<Job<State>>::builder().id(100).build(m);
648 let (_, _m) = Proxy::<Job<State>>::builder().id(101).build(m);
649 }
650
651 let server = wiremock::MockServer::start().await;
652
653 wiremock::Mock::given(wiremock::matchers::method("GET"))
654 .and(wiremock::matchers::path("/api/v0.2/jobs/"))
655 .respond_with(p.endpoint::<Job<State>>(Some(&server.uri()), None))
656 .mount(&server)
657 .await;
658
659 let jobs = make_request(server.uri(), "jobs/")
660 .await
661 .expect("failed to query jobs");
662
663 assert_eq!(jobs["results"][0]["id"], json!(100));
664 assert_eq!(jobs["results"][1]["id"], json!(101));
665 assert_eq!(jobs["results"].as_array().unwrap().len(), 2);
666
667 {
668 let m = p.mutate();
669 let (_, _m) = Proxy::<Job<State>>::builder()
670 .id(102)
671 .state(JobState::Submitted)
672 .build(m);
673 }
674
675 let jobs = make_request(server.uri(), "jobs/")
676 .await
677 .expect("failed to query jobs");
678
679 assert_eq!(jobs["results"][0]["id"], json!(100));
680 assert_eq!(jobs["results"][1]["id"], json!(101));
681 assert_eq!(jobs["results"][2]["id"], json!(102));
682 assert_eq!(jobs["results"].as_array().unwrap().len(), 3);
683
684 {
685 let mut m = p.mutate();
686 for j in m.get_iter_mut::<Job<State>>() {
687 if j.id == 102 {
688 j.state = JobState::Finished
689 }
690 }
691 }
692
693 let jobs = make_request(server.uri(), "jobs/")
694 .await
695 .expect("failed to query jobs");
696
697 assert_eq!(jobs["results"][0]["id"], json!(100));
698 assert_eq!(jobs["results"][1]["id"], json!(101));
699 assert_eq!(jobs["results"][2]["id"], json!(102));
700 assert_eq!(jobs["results"][2]["state"], json!("Finished"));
701 assert_eq!(jobs["results"].as_array().unwrap().len(), 3);
702 }
703}