Grcov report - local_run.rs

1

    use std::io::{

2

      BufRead as _,

3

      BufReader,

4

    };

5

    use std::process::Command as ProcessCommand;

6

    use std::thread;

7

8

    use anyhow::Context as _;

9

    use serde::Deserialize;

10

11

    use crate::defaults::{

12

      default_ignore_errors,

13

      default_shell,

14

      default_verbose,

15

    };

16

    use crate::handle_output;

17

    use crate::schema::{

18

      get_output_handler,

19

      TaskContext,

20

    };

21

22

    #[derive(Debug, Deserialize)]

23

    pub struct LocalRun {

24

      /// The command to run

25

      pub command: String,

26

27

      /// The shell to use to run the command

28

      #[serde(default = "default_shell")]

29

      pub shell: String,

30

31

      /// The test to run before running command

32

      /// If the test fails, the command will not run

33

      #[serde(default)]

34

      pub test: Option<String>,

35

36

      /// The working directory to run the command in

37

      #[serde(default)]

38

      pub work_dir: Option<String>,

39

40

      /// Ignore errors if the command fails

41

      #[serde(default)]

42

      pub ignore_errors: Option<bool>,

43

44

      /// Show verbose output

45

      #[serde(default)]

46

      pub verbose: Option<bool>,

47

    }

48

49

    impl LocalRun {

50
113

      pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {

51
113

        assert!(!self.command.is_empty());

52
113

        assert!(!self.shell.is_empty());

53

54
113

        let ignore_errors = self.ignore_errors(context);

55
113

        let verbose = self.verbose(context);

56
113

57
113

        // Skip the command if the test fails

58
113

        if self.test(context).is_err() {

59

          return Ok(());

60
113

        }

61
113

62
113

        let stdout = get_output_handler(verbose);

63
113

        let stderr = get_output_handler(verbose);

64
113

65
113

        let mut cmd = ProcessCommand::new(&self.shell);

66
113

        cmd.arg("-c").arg(&self.command).stdout(stdout).stderr(stderr);

67

68
113

        if let Some(work_dir) = &self.work_dir.clone() {

69

          cmd.current_dir(work_dir);

70
113

        }

71

72

        // Inject environment variables

73
113

        for (key, value) in context.env_vars.iter() {

74

          cmd.env(key, value);

75

        }

76

77
113

        let mut cmd = cmd.spawn()?;

78
113

        if verbose {

79
93

          handle_output!(cmd.stdout, context);

80
93

          handle_output!(cmd.stderr, context);

81
20

        }

82

83
113

        let status = cmd.wait()?;

84
113

        if !status.success() && !ignore_errors {

85

          anyhow::bail!("Command failed - {}", self.command);

86
113

        }

87
113

88
113

        Ok(())

89
113

      }

90

91
113

      fn test(&self, context: &TaskContext) -> anyhow::Result<()> {

92
113

        let verbose = self.verbose(context);

93
113

94
113

        let stdout = get_output_handler(verbose);

95
113

        let stderr = get_output_handler(verbose);

96

97
113

        if let Some(test) = &self.test {

98

          let mut cmd = ProcessCommand::new(&self.shell);

99

          cmd.arg("-c").arg(test).stdout(stdout).stderr(stderr);

100

101

          let mut cmd = cmd.spawn()?;

102

          if verbose {

103

            handle_output!(cmd.stdout, context);

104

            handle_output!(cmd.stderr, context);

105

          }

106

107

          let status = cmd.wait()?;

108

109

          log::trace!("Test status: {:?}", status.success());

110

          if !status.success() {

111

            anyhow::bail!("Command test failed - {}", test);

112

          }

113
113

        }

114

115
113

        Ok(())

116
113

      }

117

118
113

      fn ignore_errors(&self, context: &TaskContext) -> bool {

119
113

        self

120
113

          .ignore_errors

121
113

          .or(context.ignore_errors)

122
113

          .unwrap_or(default_ignore_errors())

123
113

      }

124

125
226

      fn verbose(&self, context: &TaskContext) -> bool {

126
226

        self.verbose.or(context.verbose).unwrap_or(default_verbose())

127
226

      }

128

    }

129

130

    #[cfg(test)]

131

    mod test {

132

      use super::*;

133

134

      #[test]

135
20

      fn test_local_run_1() -> anyhow::Result<()> {

136
20

        {

137
20

          let yaml = "

138
20

            command: echo 'Hello, World!'

139
20

            ignore_errors: false

140
20

            verbose: false

141
20

          ";

142
20

          let local_run = serde_yaml::from_str::<LocalRun>(yaml)?;

143

144
20

          assert_eq!(local_run.command, "echo 'Hello, World!'");

145
20

          assert_eq!(local_run.shell, "sh");

146
20

          assert_eq!(local_run.work_dir, None);

147
20

          assert_eq!(local_run.ignore_errors, Some(false));

148
20

          assert_eq!(local_run.verbose, Some(false));

149

150
20

          Ok(())

151

        }

152
20

      }

153

154

      #[test]

155
20

      fn test_local_run_2() -> anyhow::Result<()> {

156
20

        {

157
20

          let yaml = "

158
20

            command: echo 'Hello, World!'

159
20

            test: test $(uname) = 'Linux'

160
20

            ignore_errors: false

161
20

            verbose: false

162
20

          ";

163
20

          let local_run = serde_yaml::from_str::<LocalRun>(yaml)?;

164

165
20

          assert_eq!(local_run.command, "echo 'Hello, World!'");

166
20

          assert_eq!(local_run.test, Some("test $(uname) = 'Linux'".to_string()));

167
20

          assert_eq!(local_run.shell, "sh");

168
20

          assert_eq!(local_run.work_dir, None);

169
20

          assert_eq!(local_run.ignore_errors, Some(false));

170
20

          assert_eq!(local_run.verbose, Some(false));

171

172
20

          Ok(())

173

        }

174
20

      }

175

    }