Coverage Report

Created: 2026-06-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmAddTestCommand.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmAddTestCommand.h"
4
5
#include <algorithm>
6
7
#include <cm/memory>
8
9
#include "cmExecutionStatus.h"
10
#include "cmGeneratorExpression.h"
11
#include "cmMakefile.h"
12
#include "cmPolicies.h"
13
#include "cmStringAlgorithms.h"
14
#include "cmTest.h"
15
#include "cmTestGenerator.h"
16
17
static std::string const keywordCMP0178 = "__CMP0178";
18
19
static bool cmAddTestCommandHandleNameMode(
20
  std::vector<std::string> const& args, cmExecutionStatus& status);
21
22
bool cmAddTestCommand(std::vector<std::string> const& args,
23
                      cmExecutionStatus& status)
24
0
{
25
0
  if (!args.empty() && args[0] == "NAME") {
26
0
    return cmAddTestCommandHandleNameMode(args, status);
27
0
  }
28
29
  // First argument is the name of the test Second argument is the name of
30
  // the executable to run (a target or external program) Remaining arguments
31
  // are the arguments to pass to the executable
32
0
  if (args.size() < 2) {
33
0
    status.SetError("called with incorrect number of arguments");
34
0
    return false;
35
0
  }
36
37
0
  cmMakefile& mf = status.GetMakefile();
38
0
  cmPolicies::PolicyStatus cmp0178;
39
40
  // If the __CMP0178 keyword is present, it is always at the end
41
0
  auto endOfCommandIter =
42
0
    std::find(args.begin() + 2, args.end(), keywordCMP0178);
43
0
  if (endOfCommandIter != args.end()) {
44
0
    auto cmp0178Iter = endOfCommandIter + 1;
45
0
    if (cmp0178Iter == args.end()) {
46
0
      status.SetError(cmStrCat(keywordCMP0178, " keyword missing value"));
47
0
      return false;
48
0
    }
49
0
    if (*cmp0178Iter == "NEW") {
50
0
      cmp0178 = cmPolicies::PolicyStatus::NEW;
51
0
    } else if (*cmp0178Iter == "OLD") {
52
0
      cmp0178 = cmPolicies::PolicyStatus::OLD;
53
0
    } else {
54
0
      cmp0178 = cmPolicies::PolicyStatus::WARN;
55
0
    }
56
0
  } else {
57
0
    cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178);
58
0
  }
59
60
  // Collect the command with arguments.
61
0
  std::vector<std::string> command(args.begin() + 1, endOfCommandIter);
62
63
  // Create the test but add a generator only the first time it is
64
  // seen.  This preserves behavior from before test generators.
65
0
  cmTest* test = mf.GetTest(args[0]);
66
0
  if (test) {
67
    // If the test was already added by a new-style signature do not
68
    // allow it to be duplicated.
69
0
    if (!test->GetOldStyle()) {
70
0
      status.SetError(cmStrCat(" given test name \"", args[0],
71
0
                               "\" which already exists in this directory."));
72
0
      return false;
73
0
    }
74
0
  } else {
75
0
    test = mf.CreateTest(args[0]);
76
0
    test->SetOldStyle(true);
77
0
    test->SetCMP0178(cmp0178);
78
0
    mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test));
79
0
  }
80
0
  test->SetCommand(command);
81
82
0
  return true;
83
0
}
84
85
bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
86
                                    cmExecutionStatus& status)
87
0
{
88
0
  cmMakefile& mf = status.GetMakefile();
89
90
0
  std::string name;
91
0
  std::vector<std::string> configurations;
92
0
  std::string working_directory;
93
0
  std::vector<std::string> command;
94
0
  std::vector<std::string> buildDepends;
95
0
  bool command_expand_lists = false;
96
0
  cmPolicies::PolicyStatus cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178);
97
98
  // Read the arguments.
99
0
  enum Doing
100
0
  {
101
0
    DoingName,
102
0
    DoingCommand,
103
0
    DoingConfigs,
104
0
    DoingWorkingDirectory,
105
0
    DoingBuildDepends,
106
0
    DoingCmp0178,
107
0
    DoingNone
108
0
  };
109
0
  Doing doing = DoingName;
110
0
  for (unsigned int i = 1; i < args.size(); ++i) {
111
0
    if (args[i] == "COMMAND") {
112
0
      if (!command.empty()) {
113
0
        status.SetError(" may be given at most one COMMAND.");
114
0
        return false;
115
0
      }
116
0
      doing = DoingCommand;
117
0
    } else if (args[i] == "CONFIGURATIONS") {
118
0
      if (!configurations.empty()) {
119
0
        status.SetError(" may be given at most one set of CONFIGURATIONS.");
120
0
        return false;
121
0
      }
122
0
      doing = DoingConfigs;
123
0
    } else if (args[i] == "BUILD_DEPENDS") {
124
0
      doing = DoingBuildDepends;
125
0
    } else if (args[i] == "WORKING_DIRECTORY") {
126
0
      if (!working_directory.empty()) {
127
0
        status.SetError(" may be given at most one WORKING_DIRECTORY.");
128
0
        return false;
129
0
      }
130
0
      doing = DoingWorkingDirectory;
131
0
    } else if (args[i] == keywordCMP0178) {
132
0
      doing = DoingCmp0178;
133
0
    } else if (args[i] == "COMMAND_EXPAND_LISTS") {
134
0
      if (command_expand_lists) {
135
0
        status.SetError(" may be given at most one COMMAND_EXPAND_LISTS.");
136
0
        return false;
137
0
      }
138
0
      command_expand_lists = true;
139
0
      doing = DoingNone;
140
0
    } else if (doing == DoingName) {
141
0
      name = args[i];
142
0
      doing = DoingNone;
143
0
    } else if (doing == DoingCommand) {
144
0
      command.push_back(args[i]);
145
0
    } else if (doing == DoingConfigs) {
146
0
      configurations.push_back(args[i]);
147
0
    } else if (doing == DoingBuildDepends) {
148
0
      buildDepends.push_back(args[i]);
149
0
    } else if (doing == DoingWorkingDirectory) {
150
0
      working_directory = args[i];
151
0
      doing = DoingNone;
152
0
    } else if (doing == DoingCmp0178) {
153
0
      if (args[i] == "NEW") {
154
0
        cmp0178 = cmPolicies::PolicyStatus::NEW;
155
0
      } else if (args[i] == "OLD") {
156
0
        cmp0178 = cmPolicies::PolicyStatus::OLD;
157
0
      } else {
158
0
        cmp0178 = cmPolicies::PolicyStatus::WARN;
159
0
      }
160
0
      doing = DoingNone;
161
0
    } else {
162
0
      status.SetError(cmStrCat(" given unknown argument:\n  ", args[i], '\n'));
163
0
      return false;
164
0
    }
165
0
  }
166
167
  // Require a test name.
168
0
  if (name.empty()) {
169
0
    status.SetError(" must be given non-empty NAME.");
170
0
    return false;
171
0
  }
172
173
  // Require a command.
174
0
  if (command.empty()) {
175
0
    status.SetError(" must be given non-empty COMMAND.");
176
0
    return false;
177
0
  }
178
179
  // Require a unique test name within the directory.
180
0
  if (mf.GetTest(name)) {
181
0
    status.SetError(cmStrCat(" given test NAME \"", name,
182
0
                             "\" which already exists in this directory."));
183
0
    return false;
184
0
  }
185
186
  // Add the test.
187
0
  cmTest* test = mf.CreateTest(name);
188
0
  test->SetOldStyle(false);
189
0
  test->SetCMP0178(cmp0178);
190
0
  test->SetCommand(command);
191
0
  if (!working_directory.empty()) {
192
0
    test->SetProperty("WORKING_DIRECTORY", working_directory);
193
0
  }
194
0
  test->SetCommandExpandLists(command_expand_lists);
195
0
  if (!buildDepends.empty()) {
196
0
    if (!cmGeneratorExpression::IsValidTargetName(name)) {
197
0
      status.SetError(cmStrCat("Cannot set build dependencies for a test with"
198
0
                               " NAME \"",
199
0
                               name, "\" which is not a valid target name."));
200
0
      return false;
201
0
    }
202
0
    test->SetBuildDependencies(buildDepends);
203
0
  }
204
0
  mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test, configurations));
205
206
0
  return true;
207
0
}