summaryrefslogtreecommitdiffstats
path: root/src/export/orgmode.rs
blob: ed37b7d930d858f83c20902fd2c39599d6d4c83c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use super::Exporter;
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::benchmark::relative_speed;
use crate::export::markup::MarkupFormatter;
use crate::util::units::Unit;

use anyhow::{anyhow, Result};

#[derive(Default)]
pub struct OrgmodeFormatter;

impl MarkupFormatter for OrgmodeFormatter {
    fn table_data(&self, data: &[&str]) -> String {
        format!(
            "| {}  |  {} |\n",
            data.first().unwrap(),
            &data[1..].join(" |  ")
        )
    }

    fn table_line(&self, size: usize) -> String {
        format!("|{}--|\n", "--+".repeat(size - 1))
    }

    fn command(&self, cmd: &str) -> String {
        format!("={}=", cmd)
    }
}

#[derive(Default)]
pub struct OrgmodeExporter {}

impl Exporter for OrgmodeExporter {
    fn serialize(&self, results: &[BenchmarkResult], unit: Option<Unit>) -> Result<Vec<u8>> {
        let unit = self.unit(results, unit);
        let entries = relative_speed::compute(results);
        if entries.is_none() {
            return Err(anyhow!(
                "Relative speed comparison is not available for Emacs org-mode export."
            ));
        }

        let formatter = OrgmodeFormatter::default();
        let table = formatter.table_results(&entries.unwrap(), unit);
        Ok(table.as_bytes().to_vec())
    }
}

/// Check Emacs org-mode data row formatting
#[test]
fn test_orgmode_formatter_table_data() {
    let formatter = OrgmodeFormatter::default();
    let data = vec!["a", "b", "c"];

    let actual = formatter.table_data(&data);
    let expect = "| a  |  b |  c |\n";

    assert_eq!(expect, actual);
}

/// Check Emacs org-mode horizontal line formatting
#[test]
fn test_orgmode_formatter_table_line() {
    let formatter = OrgmodeFormatter::default();
    let size = 5;

    let actual = formatter.table_line(size);
    let expect = "|--+--+--+--+--|\n";

    assert_eq!(expect, actual);
}

/// Test helper function to create unit-based header and horizontal line
/// independently from the markup functionality for Emacs org-mode.
#[cfg(test)]
fn cfg_test_table_header(unit_short_name: String) -> String {
    format!(
        "| Command  |  Mean [{unit}] |  Min [{unit}] |  Max [{unit}] |  Relative |\n|--+--+--+--+--|\n",
        unit = unit_short_name
    )
}

/// Ensure the Emacs org-mode output includes the table header and the multiple
/// benchmark results as a table. The list of actual times is not included
/// in the output.
///
/// This also demonstrates that the first entry's units (ms) are used to set
/// the units for all entries when the time unit is not given.
#[test]
fn test_orgmode_format_ms() {
    use std::collections::BTreeMap;
    let exporter = OrgmodeExporter::default();

    let results = vec![
        BenchmarkResult {
            command: String::from("sleep 0.1"),
            mean: 0.1057,
            stddev: Some(0.0016),
            median: 0.1057,
            user: 0.0009,
            system: 0.0011,
            min: 0.1023,
            max: 0.1080,
            times: Some(vec![0.1, 0.1, 0.1]),
            exit_codes: vec![Some(0), Some(0), Some(0)],
            parameters: BTreeMap::new(),
        },
        BenchmarkResult {
            command: String::from("sleep 2"),
            mean: 2.0050,
            stddev: Some(0.0020),
            median: 2.0050,
            user: 0.0009,
            system: 0.0012,
            min: 2.0020,
            max: 2.0080,
            times: Some(vec![2.0, 2.0, 2.0]),
            exit_codes: vec![Some(0), Some(0), Some(0)],
            parameters: BTreeMap::new(),
        },
    ];

    let actual = String::from_utf8(exporter.serialize(&results, None).unwrap()).unwrap();
    let expect = format!(
        "{}\
| =sleep 0.1=  |  105.7 ± 1.6 |  102.3 |  108.0 |  1.00 |
| =sleep 2=  |  2005.0 ± 2.0 |  2002.0 |  2008.0 |  18.97 ± 0.29 |
",
        cfg_test_table_header("ms".to_string())
    );

    assert_eq!(expect, actual);
}

/// This test demonstrates that the given unit (s) is used to set
/// the units for all entries.
#[test]
fn test_orgmode_format_s() {
    use std::collections::BTreeMap;
    let exporter = OrgmodeExporter::default();

    let results = vec![
        BenchmarkResult {
            command: String::from("sleep 2"),
            mean: 2.0050,
            stddev: Some(0.0020),
            median: 2.0050,
            user: 0.0009,
            system: 0.0012,
            min: 2.0020,
            max: 2.0080,
            times: Some(vec![2.0, 2.0, 2.0]),
            exit_codes: vec![Some(0), Some(0), Some(0)],
            parameters: BTreeMap::new(),
        },
        BenchmarkResult {
            command: String::from("sleep 0.1"),
            mean: 0.1057,
            stddev: Some(0.0016),
            median: 0.1057,
            user: 0.0009,
            system: 0.0011,
            min: 0.1023,
            max: 0.1080,
            times: Some(vec![0.1, 0.1, 0.1]),
            exit_codes: vec![Some(0), Some(0), Some(0)],
            parameters: BTreeMap::new(),
        },
    ];

    let actual =
        String::from_utf8(exporter.serialize(&results, Some(Unit::Second)).unwrap()).unwrap();
    let expect = format!(
        "{}\
| =sleep 2=  |  2.005 ± 0.002 |  2.002 |  2.008 |  18.97 ± 0.29 |
| =sleep 0.1=  |  0.106 ± 0.002 |  0.102 |  0.108 |  1.00 |
",
        cfg_test_table_header("s".to_string())
    );

    assert_eq!(expect, actual);
}