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 derive_builder::Builder as DeriveBuilder;
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::SysinfoBuilder;
43/// #
44/// # fn main() -> Result<()> {
45/// let si = SysinfoBuilder::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::SysinfoBuilder;
57/// #
58/// # fn main() -> Result<()> {
59/// let si = SysinfoBuilder::default().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::SysinfoBuilder;
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 = SysinfoBuilder::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::SysinfoBuilder;
95/// #
96/// # fn main() -> Result<()> {
97/// let si = SysinfoBuilder::all_sysinfo()?;
98/// Emitter::default().idempotent().add_instructions(&si)?.emit()?;
99/// #   Ok(())
100/// # }
101/// ```
102///
103/// # Example
104/// Use [`SysinfoBuilder::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::SysinfoBuilder;
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 = SysinfoBuilder::default()
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(Clone, Copy, Debug, DeriveBuilder, PartialEq)]
155#[allow(clippy::struct_excessive_bools)]
156pub struct Sysinfo {
157    /// Set the [`RefreshKind`](sysinfo::RefreshKind) to use during sysinfo initialization.
158    ///
159    /// This allows the user to control at a more fine level what `sysinfo`
160    /// will refresh on initialization.
161    #[builder(default = "None", setter(into))]
162    refresh_kind: Option<RefreshKind>,
163    /// Enable the sysinfo name
164    #[builder(default = "false")]
165    name: bool,
166    /// Enable the sysinfo OS version
167    #[builder(default = "false")]
168    os_version: bool,
169    /// Enable sysinfo user
170    #[builder(default = "false")]
171    user: bool,
172    /// Enable sysinfo memory
173    #[builder(default = "false")]
174    memory: bool,
175    /// Enable sysinfo cpu vendor
176    #[builder(default = "false")]
177    cpu_vendor: bool,
178    /// Enable sysinfo cpu core count
179    #[builder(default = "false")]
180    cpu_core_count: bool,
181    /// Enable sysinfo cpu name
182    #[builder(default = "false")]
183    cpu_name: bool,
184    /// Enable sysinfo cpu brand
185    #[builder(default = "false")]
186    cpu_brand: bool,
187    /// Enable sysinfo cpu frequency
188    #[builder(default = "false")]
189    cpu_frequency: bool,
190    #[cfg(test)]
191    #[builder(setter(skip))]
192    fail_pid: bool,
193}
194
195impl SysinfoBuilder {
196    /// Enable all of the `VERGEN_SYSINFO_*` options
197    ///
198    /// # Errors
199    /// The underlying build function can error
200    ///
201    pub fn all_sysinfo() -> Result<Sysinfo> {
202        Self::default()
203            .name(true)
204            .os_version(true)
205            .user(true)
206            .memory(true)
207            .cpu_vendor(true)
208            .cpu_core_count(true)
209            .cpu_name(true)
210            .cpu_brand(true)
211            .cpu_frequency(true)
212            .build()
213            .map_err(Into::into)
214    }
215}
216
217impl Sysinfo {
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(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(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(VergenKey::SysinfoName, cargo_rustc_env, cargo_warning);
272            } else {
273                Self::add_sysinfo_map_entry(
274                    VergenKey::SysinfoName,
275                    idempotent,
276                    System::name(),
277                    cargo_rustc_env,
278                    cargo_warning,
279                );
280            }
281        }
282    }
283
284    fn add_sysinfo_os_verison(
285        &self,
286        _system: &System,
287        idempotent: bool,
288        cargo_rustc_env: &mut CargoRustcEnvMap,
289        cargo_warning: &mut CargoWarning,
290    ) {
291        if self.os_version {
292            if let Ok(value) = env::var(SYSINFO_OS_VERSION) {
293                add_map_entry(VergenKey::SysinfoOsVersion, value, cargo_rustc_env);
294            } else {
295                Self::add_sysinfo_map_entry(
296                    VergenKey::SysinfoOsVersion,
297                    idempotent,
298                    System::long_os_version(),
299                    cargo_rustc_env,
300                    cargo_warning,
301                );
302            }
303        }
304    }
305
306    fn add_sysinfo_user(
307        &self,
308        system: &System,
309        idempotent: bool,
310        cargo_rustc_env: &mut CargoRustcEnvMap,
311        cargo_warning: &mut CargoWarning,
312    ) {
313        if self.user {
314            if let Ok(value) = env::var(SYSINFO_USER) {
315                add_map_entry(VergenKey::SysinfoUser, value, cargo_rustc_env);
316            } else {
317                Self::add_sysinfo_map_entry(
318                    VergenKey::SysinfoUser,
319                    idempotent,
320                    self.get_user(system),
321                    cargo_rustc_env,
322                    cargo_warning,
323                );
324            }
325        }
326    }
327
328    fn get_user(&self, system: &System) -> Option<String> {
329        if let Ok(pid) = self.get_pid()
330            && let Some(process) = system.process(pid)
331        {
332            let users = Users::new_with_refreshed_list();
333            for user in &users {
334                if Self::check_user(process, user) {
335                    return Some(user.name().to_string());
336                }
337            }
338        }
339        None
340    }
341
342    fn check_user(process: &Process, user: &User) -> bool {
343        Some(user.id()) == process.user_id()
344    }
345
346    #[cfg(not(test))]
347    #[allow(clippy::unused_self)]
348    fn get_pid(&self) -> Result<Pid> {
349        get_current_pid().map_err(|e| anyhow!(format!("{e}")))
350    }
351
352    #[cfg(test)]
353    fn get_pid(&self) -> Result<Pid> {
354        if self.fail_pid {
355            Err(anyhow!("unable to determine pid"))
356        } else {
357            get_current_pid().map_err(|e| anyhow!(format!("{e}")))
358        }
359    }
360
361    fn add_sysinfo_total_memory(
362        &self,
363        system: &System,
364        idempotent: bool,
365        cargo_rustc_env: &mut CargoRustcEnvMap,
366        cargo_warning: &mut CargoWarning,
367    ) {
368        if self.memory {
369            if let Ok(value) = env::var(SYSINFO_MEMORY) {
370                add_map_entry(VergenKey::SysinfoMemory, value, cargo_rustc_env);
371            } else {
372                Self::add_sysinfo_map_entry(
373                    VergenKey::SysinfoMemory,
374                    idempotent,
375                    Some(Self::suffix(system.total_memory())),
376                    cargo_rustc_env,
377                    cargo_warning,
378                );
379            }
380        }
381    }
382
383    fn suffix(mut curr_memory: u64) -> String {
384        let mut count = 0;
385
386        while curr_memory >= 1024 {
387            curr_memory /= 1024;
388            count += 1;
389        }
390        format!(
391            "{curr_memory} {}",
392            match count {
393                0 => "B",
394                1 => "KiB",
395                2 => "MiB",
396                3 => "GiB",
397                4 => "TiB",
398                5 => "PiB",
399                // This is the highest we can reach
400                // at u64::MAX
401                _ => "EiB",
402            }
403        )
404    }
405
406    fn add_sysinfo_cpu_vendor(
407        &self,
408        system: &System,
409        idempotent: bool,
410        cargo_rustc_env: &mut CargoRustcEnvMap,
411        cargo_warning: &mut CargoWarning,
412    ) {
413        if self.cpu_vendor {
414            if let Ok(value) = env::var(SYSINFO_CPU_VENDOR) {
415                add_map_entry(VergenKey::SysinfoCpuVendor, value, cargo_rustc_env);
416            } else {
417                Self::add_sysinfo_map_entry(
418                    VergenKey::SysinfoCpuVendor,
419                    idempotent,
420                    system
421                        .cpus()
422                        .first()
423                        .map(|proc| proc.vendor_id().to_string()),
424                    cargo_rustc_env,
425                    cargo_warning,
426                );
427            }
428        }
429    }
430
431    fn add_sysinfo_cpu_core_count(
432        &self,
433        idempotent: bool,
434        cargo_rustc_env: &mut CargoRustcEnvMap,
435        cargo_warning: &mut CargoWarning,
436    ) {
437        if self.cpu_core_count {
438            if let Ok(value) = env::var(SYSINFO_CPU_CORE_COUNT) {
439                add_map_entry(VergenKey::SysinfoCpuCoreCount, value, cargo_rustc_env);
440            } else {
441                Self::add_sysinfo_map_entry(
442                    VergenKey::SysinfoCpuCoreCount,
443                    idempotent,
444                    System::physical_core_count().as_ref().map(usize::to_string),
445                    cargo_rustc_env,
446                    cargo_warning,
447                );
448            }
449        }
450    }
451
452    fn add_sysinfo_cpu_name(
453        &self,
454        system: &System,
455        idempotent: bool,
456        cargo_rustc_env: &mut CargoRustcEnvMap,
457        cargo_warning: &mut CargoWarning,
458    ) {
459        if self.cpu_name {
460            if let Ok(value) = env::var(SYSINFO_CPU_NAME) {
461                add_map_entry(VergenKey::SysinfoCpuName, value, cargo_rustc_env);
462            } else {
463                Self::add_sysinfo_map_entry(
464                    VergenKey::SysinfoCpuName,
465                    idempotent,
466                    Some(
467                        system
468                            .cpus()
469                            .iter()
470                            .map(Cpu::name)
471                            .collect::<Vec<&str>>()
472                            .join(","),
473                    ),
474                    cargo_rustc_env,
475                    cargo_warning,
476                );
477            }
478        }
479    }
480
481    fn add_sysinfo_cpu_brand(
482        &self,
483        system: &System,
484        idempotent: bool,
485        cargo_rustc_env: &mut CargoRustcEnvMap,
486        cargo_warning: &mut CargoWarning,
487    ) {
488        if self.cpu_brand {
489            if let Ok(value) = env::var(SYSINFO_CPU_BRAND) {
490                add_map_entry(VergenKey::SysinfoCpuBrand, value, cargo_rustc_env);
491            } else {
492                Self::add_sysinfo_map_entry(
493                    VergenKey::SysinfoCpuBrand,
494                    idempotent,
495                    system
496                        .cpus()
497                        .first()
498                        .map(|processor| processor.brand().to_string()),
499                    cargo_rustc_env,
500                    cargo_warning,
501                );
502            }
503        }
504    }
505
506    fn add_sysinfo_cpu_frequency(
507        &self,
508        system: &System,
509        idempotent: bool,
510        cargo_rustc_env: &mut CargoRustcEnvMap,
511        cargo_warning: &mut CargoWarning,
512    ) {
513        if self.cpu_frequency {
514            if let Ok(value) = env::var(SYSINFO_CPU_FREQUENCY) {
515                add_map_entry(VergenKey::SysinfoCpuFrequency, value, cargo_rustc_env);
516            } else {
517                Self::add_sysinfo_map_entry(
518                    VergenKey::SysinfoCpuFrequency,
519                    idempotent,
520                    system
521                        .cpus()
522                        .first()
523                        .map(|proc| proc.frequency().to_string()),
524                    cargo_rustc_env,
525                    cargo_warning,
526                );
527            }
528        }
529    }
530}
531
532impl AddEntries for Sysinfo {
533    fn add_map_entries(
534        &self,
535        idempotent: bool,
536        cargo_rustc_env: &mut CargoRustcEnvMap,
537        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
538        cargo_warning: &mut CargoWarning,
539    ) -> Result<()> {
540        if self.any() {
541            let system = Self::setup_system(self.refresh_kind);
542
543            self.add_sysinfo_name(&system, idempotent, cargo_rustc_env, cargo_warning);
544            self.add_sysinfo_os_verison(&system, idempotent, cargo_rustc_env, cargo_warning);
545            self.add_sysinfo_user(&system, idempotent, cargo_rustc_env, cargo_warning);
546            self.add_sysinfo_total_memory(&system, idempotent, cargo_rustc_env, cargo_warning);
547            self.add_sysinfo_cpu_vendor(&system, idempotent, cargo_rustc_env, cargo_warning);
548            self.add_sysinfo_cpu_core_count(idempotent, cargo_rustc_env, cargo_warning);
549            self.add_sysinfo_cpu_name(&system, idempotent, cargo_rustc_env, cargo_warning);
550            self.add_sysinfo_cpu_brand(&system, idempotent, cargo_rustc_env, cargo_warning);
551            self.add_sysinfo_cpu_frequency(&system, idempotent, cargo_rustc_env, cargo_warning);
552        }
553        Ok(())
554    }
555
556    #[cfg_attr(coverage_nightly, coverage(off))]
557    fn add_default_entries(
558        &self,
559        _config: &DefaultConfig,
560        _cargo_rustc_env_map: &mut CargoRustcEnvMap,
561        _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
562        _cargo_warning: &mut CargoWarning,
563    ) -> Result<()> {
564        // currently add_map_entries can't error for sysinfo
565        // so this will never be used.
566        Ok(())
567    }
568}
569
570#[cfg(test)]
571mod test {
572    use super::{Sysinfo, SysinfoBuilder};
573    use crate::Emitter;
574    use anyhow::Result;
575    use serial_test::serial;
576    use std::{collections::BTreeMap, io::Write};
577    use sysinfo::{CpuRefreshKind, RefreshKind};
578    use temp_env::with_var;
579    use vergen_lib::{VergenKey, count_idempotent};
580
581    const IDEM_COUNT: usize = 0;
582    const SYSINFO_COUNT: usize = 9;
583
584    #[test]
585    #[serial]
586    #[allow(clippy::clone_on_copy, clippy::redundant_clone)]
587    fn si_clone_works() -> Result<()> {
588        let si = SysinfoBuilder::all_sysinfo()?;
589        let another = si.clone();
590        assert_eq!(another, si);
591        Ok(())
592    }
593
594    #[test]
595    #[serial]
596    fn si_debug_works() -> Result<()> {
597        let si = SysinfoBuilder::all_sysinfo()?;
598        let mut buf = vec![];
599        write!(buf, "{si:?}")?;
600        assert!(!buf.is_empty());
601        Ok(())
602    }
603
604    #[test]
605    #[serial]
606    fn si_default() -> Result<()> {
607        let si = SysinfoBuilder::default().build()?;
608        let emitter = Emitter::default().add_instructions(&si)?.test_emit();
609        assert_eq!(0, emitter.cargo_rustc_env_map().len());
610        assert_eq!(0, count_idempotent(emitter.cargo_rustc_env_map()));
611        assert_eq!(0, emitter.cargo_warning().len());
612        Ok(())
613    }
614
615    #[test]
616    #[serial]
617    fn sysinfo_all_idempotent() -> Result<()> {
618        let si = SysinfoBuilder::all_sysinfo()?;
619        let config = Emitter::default()
620            .idempotent()
621            .add_instructions(&si)?
622            .test_emit();
623        assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map().len());
624        assert_eq!(
625            SYSINFO_COUNT,
626            count_idempotent(config.cargo_rustc_env_map())
627        );
628        assert_eq!(SYSINFO_COUNT, config.cargo_warning().len());
629        Ok(())
630    }
631
632    #[test]
633    #[serial]
634    fn sysinfo_all() -> Result<()> {
635        let si = SysinfoBuilder::all_sysinfo()?;
636        let config = Emitter::default().add_instructions(&si)?.test_emit();
637        assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map().len());
638        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
639        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
640        Ok(())
641    }
642
643    #[test]
644    #[serial]
645    fn sysinfo_name() -> Result<()> {
646        let si = SysinfoBuilder::default().name(true).build()?;
647        let config = Emitter::default().add_instructions(&si)?.test_emit();
648        assert_eq!(1, config.cargo_rustc_env_map().len());
649        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
650        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
651        Ok(())
652    }
653
654    #[test]
655    #[serial]
656    fn sysinfo_os_version() -> Result<()> {
657        let si = SysinfoBuilder::default().os_version(true).build()?;
658        let config = Emitter::default().add_instructions(&si)?.test_emit();
659        assert_eq!(1, config.cargo_rustc_env_map().len());
660        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
661        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
662        Ok(())
663    }
664
665    #[test]
666    #[serial]
667    fn sysinfo_user() -> Result<()> {
668        let si = SysinfoBuilder::default().user(true).build()?;
669        let config = Emitter::default().add_instructions(&si)?.test_emit();
670        assert_eq!(1, config.cargo_rustc_env_map().len());
671        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
672        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
673        Ok(())
674    }
675
676    #[test]
677    #[serial]
678    fn sysinfo_memory() -> Result<()> {
679        let si = SysinfoBuilder::default().memory(true).build()?;
680        let config = Emitter::default().add_instructions(&si)?.test_emit();
681        assert_eq!(1, config.cargo_rustc_env_map().len());
682        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
683        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
684        Ok(())
685    }
686
687    #[test]
688    #[serial]
689    fn sysinfo_cpu_vendor() -> Result<()> {
690        let si = SysinfoBuilder::default().cpu_vendor(true).build()?;
691        let config = Emitter::default().add_instructions(&si)?.test_emit();
692        assert_eq!(1, config.cargo_rustc_env_map().len());
693        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
694        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
695        Ok(())
696    }
697
698    #[test]
699    #[serial]
700    fn sysinfo_cpu_core_count() -> Result<()> {
701        let si = SysinfoBuilder::default().cpu_core_count(true).build()?;
702        let config = Emitter::default().add_instructions(&si)?.test_emit();
703        assert_eq!(1, config.cargo_rustc_env_map().len());
704        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
705        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
706        Ok(())
707    }
708
709    #[test]
710    #[serial]
711    fn sysinfo_cpu_name() -> Result<()> {
712        let si = SysinfoBuilder::default().cpu_name(true).build()?;
713        let config = Emitter::default().add_instructions(&si)?.test_emit();
714        assert_eq!(1, config.cargo_rustc_env_map().len());
715        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
716        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
717        Ok(())
718    }
719
720    #[test]
721    #[serial]
722    fn sysinfo_cpu_brand() -> Result<()> {
723        let si = SysinfoBuilder::default().cpu_brand(true).build()?;
724        let config = Emitter::default().add_instructions(&si)?.test_emit();
725        assert_eq!(1, config.cargo_rustc_env_map().len());
726        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
727        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
728        Ok(())
729    }
730
731    #[test]
732    #[serial]
733    fn sysinfo_cpu_frequency() -> Result<()> {
734        let si = SysinfoBuilder::default().cpu_frequency(true).build()?;
735        let config = Emitter::default().add_instructions(&si)?.test_emit();
736        assert_eq!(1, config.cargo_rustc_env_map().len());
737        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
738        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
739        Ok(())
740    }
741
742    #[test]
743    #[serial_test::serial]
744    fn sysinfo_refresh_kind() -> Result<()> {
745        let refresh_kind = RefreshKind::nothing();
746        let cpu_refresh_kind = CpuRefreshKind::everything()
747            .without_cpu_usage()
748            .without_frequency();
749        let si = SysinfoBuilder::default()
750            .refresh_kind(Some(refresh_kind.with_cpu(cpu_refresh_kind)))
751            .cpu_brand(true)
752            .build()?;
753        let config = Emitter::default().add_instructions(&si)?.test_emit();
754        assert_eq!(1, config.cargo_rustc_env_map().len());
755        assert_eq!(IDEM_COUNT, count_idempotent(config.cargo_rustc_env_map()));
756        assert_eq!(IDEM_COUNT, config.cargo_warning().len());
757        Ok(())
758    }
759
760    #[test]
761    #[serial]
762    fn adding_none_defaults() -> Result<()> {
763        let mut map = BTreeMap::new();
764        let mut cargo_warning = vec![];
765        Sysinfo::add_sysinfo_map_entry(
766            VergenKey::SysinfoCpuBrand,
767            false,
768            None,
769            &mut map,
770            &mut cargo_warning,
771        );
772        Ok(())
773    }
774
775    #[test]
776    #[serial]
777    fn suffix_works() {
778        assert_eq!(Sysinfo::suffix(1023), "1023 B");
779        assert_eq!(Sysinfo::suffix(1024), "1 KiB");
780        assert_eq!(Sysinfo::suffix(1_048_575), "1023 KiB");
781        assert_eq!(Sysinfo::suffix(1_048_576), "1 MiB");
782        assert_eq!(Sysinfo::suffix(1_073_741_823), "1023 MiB");
783        assert_eq!(Sysinfo::suffix(1_073_741_824), "1 GiB");
784        assert_eq!(Sysinfo::suffix(1_099_511_627_775), "1023 GiB");
785        assert_eq!(Sysinfo::suffix(1_099_511_627_776), "1 TiB");
786        assert_eq!(Sysinfo::suffix(1_125_899_906_842_623), "1023 TiB");
787        assert_eq!(Sysinfo::suffix(1_125_899_906_842_624), "1 PiB");
788        assert_eq!(
789            Sysinfo::suffix((1_125_899_906_842_624 * 1024) - 1),
790            "1023 PiB"
791        );
792        assert_eq!(Sysinfo::suffix(1_125_899_906_842_624 * 1024), "1 EiB");
793        assert_eq!(Sysinfo::suffix(u64::MAX), "15 EiB");
794    }
795
796    #[test]
797    #[serial_test::serial]
798    fn pid_lookup_fails() -> Result<()> {
799        let mut si = SysinfoBuilder::all_sysinfo()?;
800        let _ = si.fail_pid();
801        let emitter = Emitter::default().add_instructions(&si)?.test_emit();
802        assert_eq!(SYSINFO_COUNT, emitter.cargo_rustc_env_map().len());
803        assert_eq!(1, count_idempotent(emitter.cargo_rustc_env_map()));
804        assert_eq!(1, emitter.cargo_warning().len());
805        Ok(())
806    }
807
808    #[test]
809    #[serial]
810    fn sysinfo_name_override_works() {
811        with_var("VERGEN_SYSINFO_NAME", Some("this is a bad date"), || {
812            let result = || -> Result<()> {
813                let mut stdout_buf = vec![];
814                let si = SysinfoBuilder::all_sysinfo()?;
815                let _failed = Emitter::default()
816                    .add_instructions(&si)?
817                    .emit_to(&mut stdout_buf)?;
818                let output = String::from_utf8_lossy(&stdout_buf);
819                assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_NAME=this is a bad date"));
820                Ok(())
821            }();
822            assert!(result.is_ok());
823        });
824    }
825
826    #[test]
827    #[serial]
828    fn sysinfo_os_version_override_works() {
829        with_var(
830            "VERGEN_SYSINFO_OS_VERSION",
831            Some("this is a bad date"),
832            || {
833                let result =
834                    || -> Result<()> {
835                        let mut stdout_buf = vec![];
836                        let si = SysinfoBuilder::all_sysinfo()?;
837                        let _failed = Emitter::default()
838                            .add_instructions(&si)?
839                            .emit_to(&mut stdout_buf)?;
840                        let output = String::from_utf8_lossy(&stdout_buf);
841                        assert!(output.contains(
842                            "cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=this is a bad date"
843                        ));
844                        Ok(())
845                    }();
846                assert!(result.is_ok());
847            },
848        );
849    }
850
851    #[test]
852    #[serial]
853    fn sysinfo_user_override_works() {
854        with_var("VERGEN_SYSINFO_USER", Some("this is a bad date"), || {
855            let result = || -> Result<()> {
856                let mut stdout_buf = vec![];
857                let si = SysinfoBuilder::all_sysinfo()?;
858                let _failed = Emitter::default()
859                    .add_instructions(&si)?
860                    .emit_to(&mut stdout_buf)?;
861                let output = String::from_utf8_lossy(&stdout_buf);
862                assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_USER=this is a bad date"));
863                Ok(())
864            }();
865            assert!(result.is_ok());
866        });
867    }
868
869    #[test]
870    #[serial]
871    fn sysinfo_total_memory_override_works() {
872        with_var(
873            "VERGEN_SYSINFO_TOTAL_MEMORY",
874            Some("this is a bad date"),
875            || {
876                let result = || -> Result<()> {
877                    let mut stdout_buf = vec![];
878                    let si = SysinfoBuilder::all_sysinfo()?;
879                    let _failed = Emitter::default()
880                        .add_instructions(&si)?
881                        .emit_to(&mut stdout_buf)?;
882                    let output = String::from_utf8_lossy(&stdout_buf);
883                    assert!(output.contains(
884                        "cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=this is a bad date"
885                    ));
886                    Ok(())
887                }();
888                assert!(result.is_ok());
889            },
890        );
891    }
892
893    #[test]
894    #[serial]
895    fn sysinfo_cpu_vendor_override_works() {
896        with_var(
897            "VERGEN_SYSINFO_CPU_VENDOR",
898            Some("this is a bad date"),
899            || {
900                let result =
901                    || -> Result<()> {
902                        let mut stdout_buf = vec![];
903                        let si = SysinfoBuilder::all_sysinfo()?;
904                        let _failed = Emitter::default()
905                            .add_instructions(&si)?
906                            .emit_to(&mut stdout_buf)?;
907                        let output = String::from_utf8_lossy(&stdout_buf);
908                        assert!(output.contains(
909                            "cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=this is a bad date"
910                        ));
911                        Ok(())
912                    }();
913                assert!(result.is_ok());
914            },
915        );
916    }
917
918    #[test]
919    #[serial]
920    fn sysinfo_cpu_core_count_override_works() {
921        with_var(
922            "VERGEN_SYSINFO_CPU_CORE_COUNT",
923            Some("this is a bad date"),
924            || {
925                let result = || -> Result<()> {
926                    let mut stdout_buf = vec![];
927                    let si = SysinfoBuilder::all_sysinfo()?;
928                    let _failed = Emitter::default()
929                        .add_instructions(&si)?
930                        .emit_to(&mut stdout_buf)?;
931                    let output = String::from_utf8_lossy(&stdout_buf);
932                    assert!(output.contains(
933                        "cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=this is a bad date"
934                    ));
935                    Ok(())
936                }();
937                assert!(result.is_ok());
938            },
939        );
940    }
941
942    #[test]
943    #[serial]
944    fn sysinfo_cpu_name_override_works() {
945        with_var(
946            "VERGEN_SYSINFO_CPU_NAME",
947            Some("this is a bad date"),
948            || {
949                let result = || -> Result<()> {
950                    let mut stdout_buf = vec![];
951                    let si = SysinfoBuilder::all_sysinfo()?;
952                    let _failed = Emitter::default()
953                        .add_instructions(&si)?
954                        .emit_to(&mut stdout_buf)?;
955                    let output = String::from_utf8_lossy(&stdout_buf);
956                    assert!(
957                        output
958                            .contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=this is a bad date")
959                    );
960                    Ok(())
961                }();
962                assert!(result.is_ok());
963            },
964        );
965    }
966
967    #[test]
968    #[serial]
969    fn sysinfo_cpu_brand_override_works() {
970        with_var(
971            "VERGEN_SYSINFO_CPU_BRAND",
972            Some("this is a bad date"),
973            || {
974                let result =
975                    || -> Result<()> {
976                        let mut stdout_buf = vec![];
977                        let si = SysinfoBuilder::all_sysinfo()?;
978                        let _failed = Emitter::default()
979                            .add_instructions(&si)?
980                            .emit_to(&mut stdout_buf)?;
981                        let output = String::from_utf8_lossy(&stdout_buf);
982                        assert!(output.contains(
983                            "cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=this is a bad date"
984                        ));
985                        Ok(())
986                    }();
987                assert!(result.is_ok());
988            },
989        );
990    }
991
992    #[test]
993    #[serial]
994    fn sysinfo_cpu_frequency_override_works() {
995        with_var(
996            "VERGEN_SYSINFO_CPU_FREQUENCY",
997            Some("this is a bad date"),
998            || {
999                let result = || -> Result<()> {
1000                    let mut stdout_buf = vec![];
1001                    let si = SysinfoBuilder::all_sysinfo()?;
1002                    let _failed = Emitter::default()
1003                        .add_instructions(&si)?
1004                        .emit_to(&mut stdout_buf)?;
1005                    let output = String::from_utf8_lossy(&stdout_buf);
1006                    assert!(output.contains(
1007                        "cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=this is a bad date"
1008                    ));
1009                    Ok(())
1010                }();
1011                assert!(result.is_ok());
1012            },
1013        );
1014    }
1015}