Skip to main content

vergen/feature/
si.rs

1// Copyright (c) 2022 vergen developers
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use anyhow::{Result, anyhow};
10use bon::Builder;
11use std::env;
12use sysinfo::{Cpu, Pid, Process, RefreshKind, System, User, Users, get_current_pid};
13use vergen_lib::{
14    AddEntries, CargoRerunIfChanged, CargoRustcEnvMap, CargoWarning, DefaultConfig, VergenKey,
15    add_default_map_entry, add_map_entry,
16    constants::{
17        SYSINFO_CPU_BRAND, SYSINFO_CPU_CORE_COUNT, SYSINFO_CPU_FREQUENCY, SYSINFO_CPU_NAME,
18        SYSINFO_CPU_VENDOR, SYSINFO_MEMORY, SYSINFO_NAME, SYSINFO_OS_VERSION, SYSINFO_USER,
19    },
20};
21
22/// The `VERGEN_SYSINFO_*` configuration features
23///
24/// | Variable | Sample |
25/// | -------  | ------ |
26/// | `VERGEN_SYSINFO_NAME` | Manjaro Linux |
27/// | `VERGEN_SYSINFO_OS_VERSION` | Linux  Manjaro Linux |
28/// | `VERGEN_SYSINFO_USER` | Yoda |
29/// | `VERGEN_SYSINFO_TOTAL_MEMORY` | 33 GB |
30/// | `VERGEN_SYSINFO_CPU_VENDOR` | Authentic AMD |
31/// | `VERGEN_SYSINFO_CPU_CORE_COUNT` | 8 |
32/// | `VERGEN_SYSINFO_CPU_NAME` | cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7 |
33/// | `VERGEN_SYSINFO_CPU_BRAND` | AMD Ryzen Threadripper 1900X 8-Core Processor |
34/// | `VERGEN_SYSINFO_CPU_FREQUENCY` | 3792 |
35///
36/// # Example
37/// Emit all sysinfo instructions
38///
39/// ```
40/// # use anyhow::Result;
41/// # use vergen::Emitter;
42/// # use vergen::Sysinfo;
43/// #
44/// # fn main() -> Result<()> {
45/// let si = Sysinfo::all_sysinfo();
46/// Emitter::default().add_instructions(&si)?.emit()?;
47/// #   Ok(())
48/// # }
49/// ```
50///
51/// Emit some of the sysinfo instructions
52///
53/// ```
54/// # use anyhow::Result;
55/// # use vergen::Emitter;
56/// # use vergen::Sysinfo;
57/// #
58/// # fn main() -> Result<()> {
59/// let si = Sysinfo::builder().os_version(true).cpu_core_count(true).build();
60/// Emitter::default()
61///     .add_instructions(&si)?
62///     .emit()?;
63/// #   Ok(())
64/// # }
65/// ```
66///
67/// Override output with your own value
68///
69/// ```
70/// # use anyhow::Result;
71/// # use std::env;
72/// # use vergen::Emitter;
73/// # use vergen::Sysinfo;
74/// #
75/// # fn main() -> Result<()> {
76/// temp_env::with_var("VERGEN_SYSINFO_NAME", Some("this is the name I want output"), || {
77///     let result = || -> Result<()> {
78///         let si = Sysinfo::all_sysinfo();
79///         Emitter::default().add_instructions(&si)?.emit()?;
80///         Ok(())
81///     }();
82///     assert!(result.is_ok());
83/// });
84/// #   Ok(())
85/// # }
86/// ```
87///
88/// # Example
89/// This feature also recognizes the idempotent flag.
90///
91/// ```
92/// # use anyhow::Result;
93/// # use vergen::Emitter;
94/// # use vergen::Sysinfo;
95/// #
96/// # fn main() -> Result<()> {
97/// let si = Sysinfo::all_sysinfo();
98/// Emitter::default().idempotent().add_instructions(&si)?.emit()?;
99/// #   Ok(())
100/// # }
101/// ```
102///
103/// # Example
104/// Use `refresh_kind` to minimize the amount of data that [`sysinfo`] refreshes.
105///
106/// ```
107/// # use anyhow::Result;
108/// # use sysinfo::{CpuRefreshKind, RefreshKind};
109/// # use vergen::Emitter;
110/// # use vergen::Sysinfo;
111/// #
112/// # pub fn main() -> Result<()> {
113/// let refresh_kind = RefreshKind::nothing();
114/// let cpu_refresh_kind = CpuRefreshKind::everything()
115///     .without_cpu_usage()
116///     .without_frequency();
117/// let si = Sysinfo::builder()
118///     .cpu_brand(true)
119///     .refresh_kind(refresh_kind.with_cpu(cpu_refresh_kind))
120///     .build();
121/// let config = Emitter::default()
122///     .add_instructions(&si)?
123///     .emit()?;
124/// #    Ok(())
125/// # }
126/// ```
127///
128/// The above will always generate the following output
129///
130/// ```text
131/// cargo:rustc-env=VERGEN_SYSINFO_NAME=VERGEN_IDEMPOTENT_OUTPUT
132/// cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=VERGEN_IDEMPOTENT_OUTPUT
133/// cargo:rustc-env=VERGEN_SYSINFO_USER=VERGEN_IDEMPOTENT_OUTPUT
134/// cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=VERGEN_IDEMPOTENT_OUTPUT
135/// cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=VERGEN_IDEMPOTENT_OUTPUT
136/// cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=VERGEN_IDEMPOTENT_OUTPUT
137/// cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=VERGEN_IDEMPOTENT_OUTPUT
138/// cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=VERGEN_IDEMPOTENT_OUTPUT
139/// cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=VERGEN_IDEMPOTENT_OUTPUT
140/// cargo:warning=VERGEN_SYSINFO_NAME set to default
141/// cargo:warning=VERGEN_SYSINFO_OS_VERSION set to default
142/// cargo:warning=VERGEN_SYSINFO_USER set to default
143/// cargo:warning=VERGEN_SYSINFO_TOTAL_MEMORY set to default
144/// cargo:warning=VERGEN_SYSINFO_CPU_VENDOR set to default
145/// cargo:warning=VERGEN_SYSINFO_CPU_CORE_COUNT set to default
146/// cargo:warning=VERGEN_SYSINFO_CPU_NAME set to default
147/// cargo:warning=VERGEN_SYSINFO_CPU_BRAND set to default
148/// cargo:warning=VERGEN_SYSINFO_CPU_FREQUENCY set to default
149/// cargo:rerun-if-changed=build.rs
150/// cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT
151/// cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH
152/// ```
153///
154#[derive(Builder, Clone, Copy, Debug, PartialEq)]
155#[allow(clippy::struct_excessive_bools)]
156pub struct Sysinfo {
157    /// Configures the default values.
158    /// If set to `true` all defaults are in "enabled" state.
159    /// If set to `false` all defaults are in "disabled" state.
160    #[builder(field)]
161    all: bool,
162    #[cfg(test)]
163    #[builder(field)]
164    fail_pid: bool,
165    /// Set the [`RefreshKind`](sysinfo::RefreshKind) to use during sysinfo initialization.
166    ///
167    /// This allows the user to control at a more fine level what `sysinfo`
168    /// will refresh on initialization.
169    #[builder(into)]
170    refresh_kind: Option<RefreshKind>,
171    /// Enable the sysinfo name
172    #[builder(default = all)]
173    name: bool,
174    /// Enable the sysinfo OS version
175    #[builder(default = all)]
176    os_version: bool,
177    /// Enable sysinfo user
178    #[builder(default = all)]
179    user: bool,
180    /// Enable sysinfo memory
181    #[builder(default = all)]
182    memory: bool,
183    /// Enable sysinfo cpu vendor
184    #[builder(default = all)]
185    cpu_vendor: bool,
186    /// Enable sysinfo cpu core count
187    #[builder(default = all)]
188    cpu_core_count: bool,
189    /// Enable sysinfo cpu name
190    #[builder(default = all)]
191    cpu_name: bool,
192    /// Enable sysinfo cpu brand
193    #[builder(default = all)]
194    cpu_brand: bool,
195    /// Enable sysinfo cpu frequency
196    #[builder(default = all)]
197    cpu_frequency: bool,
198}
199
200impl<S: sysinfo_builder::State> SysinfoBuilder<S> {
201    /// Convenience method that switches the defaults of [`SysinfoBuilder`]
202    /// to enable all of the `VERGEN_SYSINFO_*` instructions. It can only be
203    /// called at the start of the building process, i.e. when no config
204    /// has been set yet to avoid overwrites.
205    fn all(mut self) -> Self {
206        self.all = true;
207        self
208    }
209}
210
211impl Sysinfo {
212    /// Enable all of the `VERGEN_SYSINFO_*` options
213    #[must_use]
214    pub fn all_sysinfo() -> Sysinfo {
215        Self::builder().all().build()
216    }
217
218    fn any(&self) -> bool {
219        self.name
220            || self.os_version
221            || self.user
222            || self.memory
223            || self.cpu_vendor
224            || self.cpu_core_count
225            || self.cpu_name
226            || self.cpu_brand
227            || self.cpu_frequency
228    }
229
230    #[cfg(test)]
231    pub(crate) fn fail_pid(&mut self) -> &mut Self {
232        self.fail_pid = true;
233        self
234    }
235
236    fn setup_system(refresh_kind: Option<RefreshKind>) -> System {
237        if let Some(refresh_kind) = refresh_kind {
238            let mut system = System::new();
239            system.refresh_specifics(refresh_kind);
240            system
241        } else {
242            System::new_all()
243        }
244    }
245
246    fn add_sysinfo_map_entry(
247        key: VergenKey,
248        idempotent: bool,
249        value: Option<String>,
250        cargo_rustc_env: &mut CargoRustcEnvMap,
251        cargo_warning: &mut CargoWarning,
252    ) {
253        if idempotent {
254            add_default_map_entry(idempotent, key, cargo_rustc_env, cargo_warning);
255        } else if let Some(val) = value {
256            add_map_entry(key, val, cargo_rustc_env);
257        } else {
258            add_default_map_entry(idempotent, key, cargo_rustc_env, cargo_warning);
259        }
260    }
261
262    fn add_sysinfo_name(
263        &self,
264        _system: &System,
265        idempotent: bool,
266        cargo_rustc_env: &mut CargoRustcEnvMap,
267        cargo_warning: &mut CargoWarning,
268    ) {
269        if self.name {
270            if let Ok(_value) = env::var(SYSINFO_NAME) {
271                add_default_map_entry(
272                    idempotent,
273                    VergenKey::SysinfoName,
274                    cargo_rustc_env,
275                    cargo_warning,
276                );
277            } else {
278                Self::add_sysinfo_map_entry(
279                    VergenKey::SysinfoName,
280                    idempotent,
281                    System::name(),
282                    cargo_rustc_env,
283                    cargo_warning,
284                );
285            }
286        }
287    }
288
289    fn add_sysinfo_os_verison(
290        &self,
291        _system: &System,
292        idempotent: bool,
293        cargo_rustc_env: &mut CargoRustcEnvMap,
294        cargo_warning: &mut CargoWarning,
295    ) {
296        if self.os_version {
297            if let Ok(value) = env::var(SYSINFO_OS_VERSION) {
298                add_map_entry(VergenKey::SysinfoOsVersion, value, cargo_rustc_env);
299            } else {
300                Self::add_sysinfo_map_entry(
301                    VergenKey::SysinfoOsVersion,
302                    idempotent,
303                    System::long_os_version(),
304                    cargo_rustc_env,
305                    cargo_warning,
306                );
307            }
308        }
309    }
310
311    fn add_sysinfo_user(
312        &self,
313        system: &System,
314        idempotent: bool,
315        cargo_rustc_env: &mut CargoRustcEnvMap,
316        cargo_warning: &mut CargoWarning,
317    ) {
318        if self.user {
319            if let Ok(value) = env::var(SYSINFO_USER) {
320                add_map_entry(VergenKey::SysinfoUser, value, cargo_rustc_env);
321            } else {
322                Self::add_sysinfo_map_entry(
323                    VergenKey::SysinfoUser,
324                    idempotent,
325                    self.get_user(system),
326                    cargo_rustc_env,
327                    cargo_warning,
328                );
329            }
330        }
331    }
332
333    fn get_user(&self, system: &System) -> Option<String> {
334        if let Ok(pid) = self.get_pid()
335            && let Some(process) = system.process(pid)
336        {
337            let users = Users::new_with_refreshed_list();
338            for user in &users {
339                if Self::check_user(process, user) {
340                    return Some(user.name().to_string());
341                }
342            }
343        }
344        None
345    }
346
347    fn check_user(process: &Process, user: &User) -> bool {
348        Some(user.id()) == process.user_id()
349    }
350
351    #[cfg(not(test))]
352    #[allow(clippy::unused_self)]
353    fn get_pid(&self) -> Result<Pid> {
354        get_current_pid().map_err(|e| anyhow!(format!("{e}")))
355    }
356
357    #[cfg(test)]
358    fn get_pid(&self) -> Result<Pid> {
359        if self.fail_pid {
360            Err(anyhow!("unable to determine pid"))
361        } else {
362            get_current_pid().map_err(|e| anyhow!(format!("{e}")))
363        }
364    }
365
366    fn add_sysinfo_total_memory(
367        &self,
368        system: &System,
369        idempotent: bool,
370        cargo_rustc_env: &mut CargoRustcEnvMap,
371        cargo_warning: &mut CargoWarning,
372    ) {
373        if self.memory {
374            if let Ok(value) = env::var(SYSINFO_MEMORY) {
375                add_map_entry(VergenKey::SysinfoMemory, value, cargo_rustc_env);
376            } else {
377                Self::add_sysinfo_map_entry(
378                    VergenKey::SysinfoMemory,
379                    idempotent,
380                    Some(Self::suffix(system.total_memory())),
381                    cargo_rustc_env,
382                    cargo_warning,
383                );
384            }
385        }
386    }
387
388    fn suffix(mut curr_memory: u64) -> String {
389        let mut count = 0;
390
391        while curr_memory >= 1024 {
392            curr_memory /= 1024;
393            count += 1;
394        }
395        format!(
396            "{curr_memory} {}",
397            match count {
398                0 => "B",
399                1 => "KiB",
400                2 => "MiB",
401                3 => "GiB",
402                4 => "TiB",
403                5 => "PiB",
404                // This is the highest we can reach
405                // at u64::MAX
406                _ => "EiB",
407            }
408        )
409    }
410
411    fn add_sysinfo_cpu_vendor(
412        &self,
413        system: &System,
414        idempotent: bool,
415        cargo_rustc_env: &mut CargoRustcEnvMap,
416        cargo_warning: &mut CargoWarning,
417    ) {
418        if self.cpu_vendor {
419            if let Ok(value) = env::var(SYSINFO_CPU_VENDOR) {
420                add_map_entry(VergenKey::SysinfoCpuVendor, value, cargo_rustc_env);
421            } else {
422                Self::add_sysinfo_map_entry(
423                    VergenKey::SysinfoCpuVendor,
424                    idempotent,
425                    system
426                        .cpus()
427                        .first()
428                        .map(|proc| proc.vendor_id().to_string()),
429                    cargo_rustc_env,
430                    cargo_warning,
431                );
432            }
433        }
434    }
435
436    fn add_sysinfo_cpu_core_count(
437        &self,
438        idempotent: bool,
439        cargo_rustc_env: &mut CargoRustcEnvMap,
440        cargo_warning: &mut CargoWarning,
441    ) {
442        if self.cpu_core_count {
443            if let Ok(value) = env::var(SYSINFO_CPU_CORE_COUNT) {
444                add_map_entry(VergenKey::SysinfoCpuCoreCount, value, cargo_rustc_env);
445            } else {
446                Self::add_sysinfo_map_entry(
447                    VergenKey::SysinfoCpuCoreCount,
448                    idempotent,
449                    System::physical_core_count().as_ref().map(usize::to_string),
450                    cargo_rustc_env,
451                    cargo_warning,
452                );
453            }
454        }
455    }
456
457    fn add_sysinfo_cpu_name(
458        &self,
459        system: &System,
460        idempotent: bool,
461        cargo_rustc_env: &mut CargoRustcEnvMap,
462        cargo_warning: &mut CargoWarning,
463    ) {
464        if self.cpu_name {
465            if let Ok(value) = env::var(SYSINFO_CPU_NAME) {
466                add_map_entry(VergenKey::SysinfoCpuName, value, cargo_rustc_env);
467            } else {
468                Self::add_sysinfo_map_entry(
469                    VergenKey::SysinfoCpuName,
470                    idempotent,
471                    Some(
472                        system
473                            .cpus()
474                            .iter()
475                            .map(Cpu::name)
476                            .collect::<Vec<&str>>()
477                            .join(","),
478                    ),
479                    cargo_rustc_env,
480                    cargo_warning,
481                );
482            }
483        }
484    }
485
486    fn add_sysinfo_cpu_brand(
487        &self,
488        system: &System,
489        idempotent: bool,
490        cargo_rustc_env: &mut CargoRustcEnvMap,
491        cargo_warning: &mut CargoWarning,
492    ) {
493        if self.cpu_brand {
494            if let Ok(value) = env::var(SYSINFO_CPU_BRAND) {
495                add_map_entry(VergenKey::SysinfoCpuBrand, value, cargo_rustc_env);
496            } else {
497                Self::add_sysinfo_map_entry(
498                    VergenKey::SysinfoCpuBrand,
499                    idempotent,
500                    system
501                        .cpus()
502                        .first()
503                        .map(|processor| processor.brand().to_string()),
504                    cargo_rustc_env,
505                    cargo_warning,
506                );
507            }
508        }
509    }
510
511    fn add_sysinfo_cpu_frequency(
512        &self,
513        system: &System,
514        idempotent: bool,
515        cargo_rustc_env: &mut CargoRustcEnvMap,
516        cargo_warning: &mut CargoWarning,
517    ) {
518        if self.cpu_frequency {
519            if let Ok(value) = env::var(SYSINFO_CPU_FREQUENCY) {
520                add_map_entry(VergenKey::SysinfoCpuFrequency, value, cargo_rustc_env);
521            } else {
522                Self::add_sysinfo_map_entry(
523                    VergenKey::SysinfoCpuFrequency,
524                    idempotent,
525                    system
526                        .cpus()
527                        .first()
528                        .map(|proc| proc.frequency().to_string()),
529                    cargo_rustc_env,
530                    cargo_warning,
531                );
532            }
533        }
534    }
535}
536
537impl AddEntries for Sysinfo {
538    fn add_map_entries(
539        &self,
540        idempotent: bool,
541        cargo_rustc_env: &mut CargoRustcEnvMap,
542        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
543        cargo_warning: &mut CargoWarning,
544    ) -> Result<()> {
545        if self.any() {
546            let system = Self::setup_system(self.refresh_kind);
547
548            self.add_sysinfo_name(&system, idempotent, cargo_rustc_env, cargo_warning);
549            self.add_sysinfo_os_verison(&system, idempotent, cargo_rustc_env, cargo_warning);
550            self.add_sysinfo_user(&system, idempotent, cargo_rustc_env, cargo_warning);
551            self.add_sysinfo_total_memory(&system, idempotent, cargo_rustc_env, cargo_warning);
552            self.add_sysinfo_cpu_vendor(&system, idempotent, cargo_rustc_env, cargo_warning);
553            self.add_sysinfo_cpu_core_count(idempotent, cargo_rustc_env, cargo_warning);
554            self.add_sysinfo_cpu_name(&system, idempotent, cargo_rustc_env, cargo_warning);
555            self.add_sysinfo_cpu_brand(&system, idempotent, cargo_rustc_env, cargo_warning);
556            self.add_sysinfo_cpu_frequency(&system, idempotent, cargo_rustc_env, cargo_warning);
557        }
558        Ok(())
559    }
560
561    #[cfg_attr(all(nightly, coverage_nightly), coverage(off))]
562    fn add_default_entries(
563        &self,
564        _config: &DefaultConfig,
565        _cargo_rustc_env_map: &mut CargoRustcEnvMap,
566        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
567        _cargo_warning: &mut CargoWarning,
568    ) -> Result<()> {
569        // currently add_map_entries can't error for sysinfo
570        // so this will never be used.
571        Ok(())
572    }
573}
574
575#[cfg(test)]
576mod test {
577    use super::Sysinfo;
578    use crate::Emitter;
579    use anyhow::Result;
580    use serial_test::serial;
581    use std::{collections::BTreeMap, io::Write};
582    use sysinfo::{CpuRefreshKind, RefreshKind};
583    use temp_env::with_var;
584    use vergen_lib::{VergenKey, count_idempotent};
585
586    const IDEM_COUNT: usize = 0;
587    const SYSINFO_COUNT: usize = 9;
588
589    #[test]
590    #[serial]
591    #[allow(clippy::clone_on_copy, clippy::redundant_clone)]
592    fn si_clone_works() -> Result<()> {
593        let si = Sysinfo::all_sysinfo();
594        let another = si.clone();
595        assert_eq!(another, si);
596        Ok(())
597    }
598
599    #[test]
600    #[serial]
601    fn si_debug_works() -> Result<()> {
602        let si = Sysinfo::all_sysinfo();
603        let mut buf = vec![];
604        write!(buf, "{si:?}")?;
605        assert!(!buf.is_empty());
606        Ok(())
607    }
608
609    #[test]
610    #[serial]
611    fn si_default() -> Result<()> {
612        let si = Sysinfo::builder().build();
613        let emitter = Emitter::default().add_instructions(&si)?.test_emit();
614        assert_eq!(0, emitter.cargo_rustc_env_map().len());
615        assert_eq!(0, count_idempotent(emitter.cargo_rustc_env_map()));
616        assert_eq!(0, emitter.cargo_warning().len());
617        Ok(())
618    }
619
620    #[test]
621    #[serial]
622    fn sysinfo_all_idempotent() -> Result<()> {
623        let si = Sysinfo::all_sysinfo();
624        let config = Emitter::default()
625            .idempotent()
626            .add_instructions(&si)?
627            .test_emit();
628        assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map().len());
629        assert_eq!(
630            SYSINFO_COUNT,
631            count_idempotent(config.cargo_rustc_env_map())
632        );
633        assert_eq!(SYSINFO_COUNT, config.cargo_warning().len());
634        Ok(())
635    }
636
637    #[test]
638    #[serial]
639    fn sysinfo_all() -> Result<()> {
640        let si = Sysinfo::all_sysinfo();
641        let config = Emitter::default().add_instructions(&si)?.test_emit();
642        assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map().len());
643        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
644        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
645        Ok(())
646    }
647
648    #[test]
649    #[serial]
650    fn sysinfo_name() -> Result<()> {
651        let si = Sysinfo::builder().name(true).build();
652        let config = Emitter::default().add_instructions(&si)?.test_emit();
653        assert_eq!(1, config.cargo_rustc_env_map().len());
654        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
655        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
656        Ok(())
657    }
658
659    #[test]
660    #[serial]
661    fn sysinfo_os_version() -> Result<()> {
662        let si = Sysinfo::builder().os_version(true).build();
663        let config = Emitter::default().add_instructions(&si)?.test_emit();
664        assert_eq!(1, config.cargo_rustc_env_map().len());
665        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
666        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
667        Ok(())
668    }
669
670    #[test]
671    #[serial]
672    fn sysinfo_user() -> Result<()> {
673        let si = Sysinfo::builder().user(true).build();
674        let config = Emitter::default().add_instructions(&si)?.test_emit();
675        assert_eq!(1, config.cargo_rustc_env_map().len());
676        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
677        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
678        Ok(())
679    }
680
681    #[test]
682    #[serial]
683    fn sysinfo_memory() -> Result<()> {
684        let si = Sysinfo::builder().memory(true).build();
685        let config = Emitter::default().add_instructions(&si)?.test_emit();
686        assert_eq!(1, config.cargo_rustc_env_map().len());
687        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
688        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
689        Ok(())
690    }
691
692    #[test]
693    #[serial]
694    fn sysinfo_cpu_vendor() -> Result<()> {
695        let si = Sysinfo::builder().cpu_vendor(true).build();
696        let config = Emitter::default().add_instructions(&si)?.test_emit();
697        assert_eq!(1, config.cargo_rustc_env_map().len());
698        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
699        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
700        Ok(())
701    }
702
703    #[test]
704    #[serial]
705    fn sysinfo_cpu_core_count() -> Result<()> {
706        let si = Sysinfo::builder().cpu_core_count(true).build();
707        let config = Emitter::default().add_instructions(&si)?.test_emit();
708        assert_eq!(1, config.cargo_rustc_env_map().len());
709        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
710        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
711        Ok(())
712    }
713
714    #[test]
715    #[serial]
716    fn sysinfo_cpu_name() -> Result<()> {
717        let si = Sysinfo::builder().cpu_name(true).build();
718        let config = Emitter::default().add_instructions(&si)?.test_emit();
719        assert_eq!(1, config.cargo_rustc_env_map().len());
720        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
721        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
722        Ok(())
723    }
724
725    #[test]
726    #[serial]
727    fn sysinfo_cpu_brand() -> Result<()> {
728        let si = Sysinfo::builder().cpu_brand(true).build();
729        let config = Emitter::default().add_instructions(&si)?.test_emit();
730        assert_eq!(1, config.cargo_rustc_env_map().len());
731        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
732        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
733        Ok(())
734    }
735
736    #[test]
737    #[serial]
738    fn sysinfo_cpu_frequency() -> Result<()> {
739        let si = Sysinfo::builder().cpu_frequency(true).build();
740        let config = Emitter::default().add_instructions(&si)?.test_emit();
741        assert_eq!(1, config.cargo_rustc_env_map().len());
742        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
743        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
744        Ok(())
745    }
746
747    #[test]
748    #[serial_test::serial]
749    fn sysinfo_refresh_kind() -> Result<()> {
750        let refresh_kind = RefreshKind::nothing();
751        let cpu_refresh_kind = CpuRefreshKind::everything()
752            .without_cpu_usage()
753            .without_frequency();
754        let si = Sysinfo::builder()
755            .refresh_kind(refresh_kind.with_cpu(cpu_refresh_kind))
756            .cpu_brand(true)
757            .build();
758        let config = Emitter::default().add_instructions(&si)?.test_emit();
759        assert_eq!(1, config.cargo_rustc_env_map().len());
760        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
761        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
762        Ok(())
763    }
764
765    #[test]
766    #[serial]
767    fn adding_none_defaults() -> Result<()> {
768        let mut map = BTreeMap::new();
769        let mut cargo_warning = vec![];
770        Sysinfo::add_sysinfo_map_entry(
771            VergenKey::SysinfoCpuBrand,
772            false,
773            None,
774            &mut map,
775            &mut cargo_warning,
776        );
777        Ok(())
778    }
779
780    #[test]
781    #[serial]
782    fn suffix_works() {
783        assert_eq!(Sysinfo::suffix(1023), "1023 B");
784        assert_eq!(Sysinfo::suffix(1024), "1 KiB");
785        assert_eq!(Sysinfo::suffix(1_048_575), "1023 KiB");
786        assert_eq!(Sysinfo::suffix(1_048_576), "1 MiB");
787        assert_eq!(Sysinfo::suffix(1_073_741_823), "1023 MiB");
788        assert_eq!(Sysinfo::suffix(1_073_741_824), "1 GiB");
789        assert_eq!(Sysinfo::suffix(1_099_511_627_775), "1023 GiB");
790        assert_eq!(Sysinfo::suffix(1_099_511_627_776), "1 TiB");
791        assert_eq!(Sysinfo::suffix(1_125_899_906_842_623), "1023 TiB");
792        assert_eq!(Sysinfo::suffix(1_125_899_906_842_624), "1 PiB");
793        assert_eq!(
794            Sysinfo::suffix((1_125_899_906_842_624 * 1024) - 1),
795            "1023 PiB"
796        );
797        assert_eq!(Sysinfo::suffix(1_125_899_906_842_624 * 1024), "1 EiB");
798        assert_eq!(Sysinfo::suffix(u64::MAX), "15 EiB");
799    }
800
801    #[test]
802    #[serial_test::serial]
803    fn pid_lookup_fails() -> Result<()> {
804        let mut si = Sysinfo::all_sysinfo();
805        let _ = si.fail_pid();
806        let emitter = Emitter::default().add_instructions(&si)?.test_emit();
807        assert_eq!(8, emitter.cargo_rustc_env_map().len());
808        assert_eq!(0, count_idempotent(emitter.cargo_rustc_env_map()));
809        assert_eq!(1, emitter.cargo_warning().len());
810        Ok(())
811    }
812
813    #[test]
814    #[serial_test::serial]
815    fn pid_lookup_fails_idempotent() -> Result<()> {
816        let mut si = Sysinfo::all_sysinfo();
817        let _ = si.fail_pid();
818        let emitter = Emitter::default()
819            .idempotent()
820            .add_instructions(&si)?
821            .test_emit();
822        assert_eq!(SYSINFO_COUNT, emitter.cargo_rustc_env_map().len());
823        assert_eq!(
824            SYSINFO_COUNT,
825            count_idempotent(emitter.cargo_rustc_env_map())
826        );
827        assert_eq!(SYSINFO_COUNT, emitter.cargo_warning().len());
828        Ok(())
829    }
830
831    #[test]
832    #[serial]
833    fn sysinfo_name_override_works() {
834        with_var("VERGEN_SYSINFO_NAME", Some("this is a bad date"), || {
835            let result = || -> Result<()> {
836                let mut stdout_buf = vec![];
837                let si = Sysinfo::all_sysinfo();
838                let _failed = Emitter::default()
839                    .add_instructions(&si)?
840                    .emit_to(&mut stdout_buf)?;
841                let output = String::from_utf8_lossy(&stdout_buf);
842                assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_NAME=this is a bad date"));
843                Ok(())
844            }();
845            assert!(result.is_ok());
846        });
847    }
848
849    #[test]
850    #[serial]
851    fn sysinfo_os_version_override_works() {
852        with_var(
853            "VERGEN_SYSINFO_OS_VERSION",
854            Some("this is a bad date"),
855            || {
856                let result =
857                    || -> Result<()> {
858                        let mut stdout_buf = vec![];
859                        let si = Sysinfo::all_sysinfo();
860                        let _failed = Emitter::default()
861                            .add_instructions(&si)?
862                            .emit_to(&mut stdout_buf)?;
863                        let output = String::from_utf8_lossy(&stdout_buf);
864                        assert!(output.contains(
865                            "cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=this is a bad date"
866                        ));
867                        Ok(())
868                    }();
869                assert!(result.is_ok());
870            },
871        );
872    }
873
874    #[test]
875    #[serial]
876    fn sysinfo_user_override_works() {
877        with_var("VERGEN_SYSINFO_USER", Some("this is a bad date"), || {
878            let result = || -> Result<()> {
879                let mut stdout_buf = vec![];
880                let si = Sysinfo::all_sysinfo();
881                let _failed = Emitter::default()
882                    .add_instructions(&si)?
883                    .emit_to(&mut stdout_buf)?;
884                let output = String::from_utf8_lossy(&stdout_buf);
885                assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_USER=this is a bad date"));
886                Ok(())
887            }();
888            assert!(result.is_ok());
889        });
890    }
891
892    #[test]
893    #[serial]
894    fn sysinfo_total_memory_override_works() {
895        with_var(
896            "VERGEN_SYSINFO_TOTAL_MEMORY",
897            Some("this is a bad date"),
898            || {
899                let result = || -> Result<()> {
900                    let mut stdout_buf = vec![];
901                    let si = Sysinfo::all_sysinfo();
902                    let _failed = Emitter::default()
903                        .add_instructions(&si)?
904                        .emit_to(&mut stdout_buf)?;
905                    let output = String::from_utf8_lossy(&stdout_buf);
906                    assert!(output.contains(
907                        "cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=this is a bad date"
908                    ));
909                    Ok(())
910                }();
911                assert!(result.is_ok());
912            },
913        );
914    }
915
916    #[test]
917    #[serial]
918    fn sysinfo_cpu_vendor_override_works() {
919        with_var(
920            "VERGEN_SYSINFO_CPU_VENDOR",
921            Some("this is a bad date"),
922            || {
923                let result =
924                    || -> Result<()> {
925                        let mut stdout_buf = vec![];
926                        let si = Sysinfo::all_sysinfo();
927                        let _failed = Emitter::default()
928                            .add_instructions(&si)?
929                            .emit_to(&mut stdout_buf)?;
930                        let output = String::from_utf8_lossy(&stdout_buf);
931                        assert!(output.contains(
932                            "cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=this is a bad date"
933                        ));
934                        Ok(())
935                    }();
936                assert!(result.is_ok());
937            },
938        );
939    }
940
941    #[test]
942    #[serial]
943    fn sysinfo_cpu_core_count_override_works() {
944        with_var(
945            "VERGEN_SYSINFO_CPU_CORE_COUNT",
946            Some("this is a bad date"),
947            || {
948                let result = || -> Result<()> {
949                    let mut stdout_buf = vec![];
950                    let si = Sysinfo::all_sysinfo();
951                    let _failed = Emitter::default()
952                        .add_instructions(&si)?
953                        .emit_to(&mut stdout_buf)?;
954                    let output = String::from_utf8_lossy(&stdout_buf);
955                    assert!(output.contains(
956                        "cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=this is a bad date"
957                    ));
958                    Ok(())
959                }();
960                assert!(result.is_ok());
961            },
962        );
963    }
964
965    #[test]
966    #[serial]
967    fn sysinfo_cpu_name_override_works() {
968        with_var(
969            "VERGEN_SYSINFO_CPU_NAME",
970            Some("this is a bad date"),
971            || {
972                let result = || -> Result<()> {
973                    let mut stdout_buf = vec![];
974                    let si = Sysinfo::all_sysinfo();
975                    let _failed = Emitter::default()
976                        .add_instructions(&si)?
977                        .emit_to(&mut stdout_buf)?;
978                    let output = String::from_utf8_lossy(&stdout_buf);
979                    assert!(
980                        output
981                            .contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=this is a bad date")
982                    );
983                    Ok(())
984                }();
985                assert!(result.is_ok());
986            },
987        );
988    }
989
990    #[test]
991    #[serial]
992    fn sysinfo_cpu_brand_override_works() {
993        with_var(
994            "VERGEN_SYSINFO_CPU_BRAND",
995            Some("this is a bad date"),
996            || {
997                let result =
998                    || -> Result<()> {
999                        let mut stdout_buf = vec![];
1000                        let si = Sysinfo::all_sysinfo();
1001                        let _failed = Emitter::default()
1002                            .add_instructions(&si)?
1003                            .emit_to(&mut stdout_buf)?;
1004                        let output = String::from_utf8_lossy(&stdout_buf);
1005                        assert!(output.contains(
1006                            "cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=this is a bad date"
1007                        ));
1008                        Ok(())
1009                    }();
1010                assert!(result.is_ok());
1011            },
1012        );
1013    }
1014
1015    #[test]
1016    #[serial]
1017    fn sysinfo_cpu_frequency_override_works() {
1018        with_var(
1019            "VERGEN_SYSINFO_CPU_FREQUENCY",
1020            Some("this is a bad date"),
1021            || {
1022                let result = || -> Result<()> {
1023                    let mut stdout_buf = vec![];
1024                    let si = Sysinfo::all_sysinfo();
1025                    let _failed = Emitter::default()
1026                        .add_instructions(&si)?
1027                        .emit_to(&mut stdout_buf)?;
1028                    let output = String::from_utf8_lossy(&stdout_buf);
1029                    assert!(output.contains(
1030                        "cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=this is a bad date"
1031                    ));
1032                    Ok(())
1033                }();
1034                assert!(result.is_ok());
1035            },
1036        );
1037    }
1038}