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