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