• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 11335495770

14 Oct 2024 09:47PM UTC coverage: 84.223% (-0.6%) from 84.776%
11335495770

push

github

web-flow
[pre-commit.ci] pre-commit autoupdate (#3650)

updates:
- [github.com/psf/black: 24.8.0 → 24.10.0](https://github.com/psf/black/compare/24.8.0...24.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

19005 of 22565 relevant lines covered (84.22%)

23473.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.95
/src/TDB2.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
// cmake.h include header must come first
29

30
#include <Color.h>
31
#include <Context.h>
32
#include <Datetime.h>
33
#include <TDB2.h>
34
#include <Table.h>
35
#include <format.h>
36
#include <main.h>
37
#include <shared.h>
38
#include <signal.h>
39
#include <stdlib.h>
40
#include <util.h>
41

42
#include <algorithm>
43
#include <iostream>
44
#include <list>
45
#include <sstream>
46
#include <unordered_set>
47
#include <vector>
48

49
bool TDB2::debug_mode = false;
50
static void dependency_scan(std::vector<Task>&);
51

52
////////////////////////////////////////////////////////////////////////////////
53
void TDB2::open_replica(const std::string& location, bool create_if_missing) {
4,499✔
54
  _replica = tc::new_replica_on_disk(location, create_if_missing);
4,500✔
55
}
4,498✔
56

57
////////////////////////////////////////////////////////////////////////////////
58
// Add the new task to the replica.
59
void TDB2::add(Task& task) {
2,986✔
60
  // Ensure the task is consistent, and provide defaults if necessary.
61
  // bool argument to validate() is "applyDefault", to apply default values for
62
  // properties not otherwise given.
63
  task.validate(true);
2,986✔
64

65
  rust::Vec<tc::Operation> ops;
2,985✔
66
  maybe_add_undo_point(ops);
2,985✔
67

68
  auto uuid = task.get("uuid");
2,985✔
69
  changes[uuid] = task;
2,985✔
70
  tc::Uuid tcuuid = tc::uuid_from_string(uuid);
2,985✔
71

72
  // run hooks for this new task
73
  Context::getContext().hooks.onAdd(task);
2,985✔
74

75
  auto taskdata = tc::create_task(tcuuid, ops);
2,978✔
76

77
  // add the task attributes
78
  for (auto& attr : task.all()) {
19,783✔
79
    // TaskChampion does not store uuid or id in the task data
80
    if (attr == "uuid" || attr == "id") {
16,805✔
81
      continue;
2,978✔
82
    }
83

84
    taskdata->update(attr, task.get(attr), ops);
13,827✔
85
  }
2,978✔
86
  replica()->commit_operations(std::move(ops));
2,978✔
87

88
  invalidate_cached_info();
2,978✔
89

90
  // get the ID that was assigned to this task
91
  auto id = working_set()->by_uuid(tcuuid);
2,978✔
92
  if (id > 0) {
2,978✔
93
    task.id = id;
2,858✔
94
  }
95
}
2,992✔
96

97
////////////////////////////////////////////////////////////////////////////////
98
// Modify the task in storage to match the given task.
99
//
100
// Note that there are a few race conditions to consider here.  Taskwarrior
101
// loads the enitre task into memory and this method then essentially writes
102
// the entire task back to the database. So, if the task in the database
103
// changes between loading the task and this method being called, this method
104
// will "revert" those changes. In practice this would only occur when multiple
105
// `task` invocatoins run at the same time and try to modify the same task.
106
//
107
// There is also the possibility that another task process has deleted the task
108
// from the database between the time this process loaded the tsak and called
109
// this method. In this case, this method throws an error that will make sense
110
// to the user. This is especially unlikely since tasks are only deleted when
111
// they have been unmodified for a long time.
112
void TDB2::modify(Task& task) {
576✔
113
  // All locally modified tasks are timestamped, implicitly overwriting any
114
  // changes the user or hooks tried to apply to the "modified" attribute.
115
  task.setAsNow("modified");
576✔
116
  task.validate(false);
576✔
117
  auto uuid = task.get("uuid");
576✔
118

119
  rust::Vec<tc::Operation> ops;
576✔
120
  maybe_add_undo_point(ops);
576✔
121

122
  changes[uuid] = task;
576✔
123

124
  // invoke the hook and allow it to modify the task before updating
125
  Task original;
576✔
126
  get(uuid, original);
576✔
127
  Context::getContext().hooks.onModify(original, task);
576✔
128

129
  tc::Uuid tcuuid = tc::uuid_from_string(uuid);
570✔
130
  auto maybe_tctask = replica()->get_task_data(tcuuid);
570✔
131
  if (maybe_tctask.is_none()) {
570✔
132
    throw std::string("task no longer exists");
×
133
  }
134
  auto tctask = maybe_tctask.take();
570✔
135

136
  // Perform the necessary `update` operations to set all keys in `tctask`
137
  // equal to those in `task`.
138
  std::unordered_set<std::string> seen;
570✔
139
  for (auto k : task.all()) {
4,948✔
140
    // ignore task keys that aren't stored
141
    if (k == "uuid") {
4,378✔
142
      continue;
570✔
143
    }
144
    seen.insert(k);
3,808✔
145
    bool update = false;
3,808✔
146
    auto v_new = task.get(k);
3,808✔
147
    std::string v_tctask;
3,808✔
148
    if (tctask->get(k, v_tctask)) {
3,808✔
149
      update = v_tctask != v_new;
3,266✔
150
    } else {
151
      // tctask does not contain k, so update it
152
      update = true;
542✔
153
    }
154
    if (update) {
3,808✔
155
      // An empty string indicates the value should be removed.
156
      if (v_new == "") {
969✔
157
        tctask->update_remove(k, ops);
6✔
158
      } else {
159
        tctask->update(k, v_new, ops);
963✔
160
      }
161
    }
162
  }
4,948✔
163

164
  // we've now added and updated properties; but must find any deleted properties
165
  for (auto k : tctask->properties()) {
4,432✔
166
    auto kstr = static_cast<std::string>(k);
3,862✔
167
    if (seen.find(kstr) == seen.end()) {
3,862✔
168
      tctask->update_remove(kstr, ops);
60✔
169
    }
170
  }
4,432✔
171

172
  replica()->commit_operations(std::move(ops));
570✔
173

174
  invalidate_cached_info();
570✔
175
}
588✔
176

177
////////////////////////////////////////////////////////////////////////////////
178
void TDB2::purge(Task& task) {
6✔
179
  auto uuid = tc::uuid_from_string(task.get("uuid"));
6✔
180
  rust::Vec<tc::Operation> ops;
6✔
181
  auto maybe_tctask = replica()->get_task_data(uuid);
6✔
182
  if (maybe_tctask.is_some()) {
6✔
183
    auto tctask = maybe_tctask.take();
6✔
184
    tctask->delete_task(ops);
6✔
185
    replica()->commit_operations(std::move(ops));
6✔
186
  }
6✔
187

188
  invalidate_cached_info();
6✔
189
}
6✔
190

191
////////////////////////////////////////////////////////////////////////////////
192
rust::Box<tc::Replica>& TDB2::replica() {
758,079✔
193
  // Create a replica in-memory if `open_replica` has not been called. This
194
  // occurs in tests.
195
  if (!_replica) {
758,079✔
196
    _replica = tc::new_replica_in_memory();
1✔
197
  }
198
  return _replica.value();
758,079✔
199
}
200

201
////////////////////////////////////////////////////////////////////////////////
202
const rust::Box<tc::WorkingSet>& TDB2::working_set() {
754,406✔
203
  if (!_working_set.has_value()) {
754,406✔
204
    _working_set = replica()->working_set();
5,204✔
205
  }
206
  return _working_set.value();
754,406✔
207
}
208

209
////////////////////////////////////////////////////////////////////////////////
210
void TDB2::maybe_add_undo_point(rust::Vec<tc::Operation>& ops) {
3,561✔
211
  // Only add an UndoPoint if there are not yet any changes.
212
  if (changes.size() == 0) {
3,561✔
213
    tc::add_undo_point(ops);
1,775✔
214
  }
215
}
3,561✔
216

217
////////////////////////////////////////////////////////////////////////////////
218
void TDB2::get_changes(std::vector<Task>& changes) {
5✔
219
  std::map<std::string, Task>& changes_map = this->changes;
5✔
220
  changes.clear();
5✔
221
  std::transform(changes_map.begin(), changes_map.end(), std::back_inserter(changes),
5✔
222
                 [](const auto& kv) { return kv.second; });
1✔
223
}
5✔
224

225
////////////////////////////////////////////////////////////////////////////////
226
void TDB2::revert() {
7✔
227
  rust::Vec<tc::Operation> undo_ops = replica()->get_undo_operations();
7✔
228
  if (undo_ops.size() == 0) {
7✔
229
    std::cout << "No operations to undo.\n";
×
230
    return;
×
231
  }
232
  if (confirm_revert(undo_ops)) {
7✔
233
    // Note that commit_reversed_operations rebuilds the working set, so that
234
    // need not be done here.
235
    if (!replica()->commit_reversed_operations(std::move(undo_ops))) {
4✔
236
      std::cout << "Could not undo: other operations have occurred.";
×
237
    }
238
  }
239
}
7✔
240

241
////////////////////////////////////////////////////////////////////////////////
242
bool TDB2::confirm_revert(rust::Vec<tc::Operation>& undo_ops) {
7✔
243
  // TODO Use show_diff rather than this basic listing of operations, though
244
  // this might be a worthy undo.style itself.
245

246
  // Count non-undo operations
247
  int ops_count = 0;
7✔
248
  for (auto& op : undo_ops) {
40✔
249
    if (!op.is_undo_point()) {
33✔
250
      ops_count++;
26✔
251
    }
252
  }
253

254
  std::cout << "The following " << ops_count << " operations would be reverted:\n";
7✔
255
  for (auto& op : undo_ops) {
40✔
256
    if (op.is_undo_point()) {
33✔
257
      continue;
7✔
258
    }
259

260
    std::cout << "- ";
26✔
261
    std::string uuid = static_cast<std::string>(op.get_uuid().to_string());
26✔
262
    if (op.is_create()) {
26✔
263
      std::cout << "Create " << uuid;
2✔
264
    } else if (op.is_delete()) {
24✔
265
      std::cout << "Delete ";
×
266
      auto old_task = op.get_old_task();
×
267
      bool found_description = false;
×
268
      for (auto& pv : old_task) {
×
269
        if (static_cast<std::string>(pv.prop) == "description") {
×
270
          std::cout << static_cast<std::string>(pv.value) << " (" << uuid << ")";
×
271
          found_description = true;
×
272
        }
273
      }
274
      if (!found_description) {
×
275
        std::cout << uuid;
×
276
      }
277
    } else if (op.is_update()) {
24✔
278
      std::cout << "Update " << uuid << "\n";
24✔
279
      std::string property;
24✔
280
      op.get_property(property);
24✔
281
      std::string value;
24✔
282
      bool have_value = op.get_value(value);
24✔
283
      std::string old_value;
24✔
284
      bool have_old_value = op.get_old_value(old_value);
24✔
285
      std::cout << "    " << property << ": ";
24✔
286
      std::cout << (have_old_value ? old_value : "<empty>") << " -> ";
40✔
287
      std::cout << (have_value ? value : "<empty>");
27✔
288
    }
24✔
289
    std::cout << "\n";
26✔
290
  }
26✔
291
  return !Context::getContext().config.getBoolean("confirmation") ||
34✔
292
         confirm(
19✔
293
             "The undo command is not reversible.  Are you sure you want to revert to the previous "
294
             "state?");
7✔
295
  return true;
296
}
297

298
////////////////////////////////////////////////////////////////////////////////
299
void TDB2::show_diff(const std::string& current, const std::string& prior,
×
300
                     const std::string& when) {
301
  Datetime lastChange(strtoll(when.c_str(), nullptr, 10));
×
302

303
  // Set the colors.
304
  Color color_red(
305
      Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : "");
×
306
  Color color_green(
307
      Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : "");
×
308

309
  auto before = prior == "" ? Task() : Task(prior);
×
310
  auto after = Task(current);
×
311

312
  if (Context::getContext().config.get("undo.style") == "side") {
×
313
    Table view = before.diffForUndoSide(after);
×
314

315
    std::cout << '\n'
316
              << format("The last modification was made {1}", lastChange.toString()) << '\n'
×
317
              << '\n'
318
              << view.render() << '\n';
×
319
  }
×
320

321
  else if (Context::getContext().config.get("undo.style") == "diff") {
×
322
    Table view = before.diffForUndoPatch(after, lastChange);
×
323
    std::cout << '\n' << view.render() << '\n';
×
324
  }
×
325
}
×
326

327
////////////////////////////////////////////////////////////////////////////////
328
void TDB2::gc() {
913✔
329
  Timer timer;
913✔
330

331
  // Allowed as an override, but not recommended.
332
  if (Context::getContext().config.getBoolean("gc")) {
2,739✔
333
    replica()->rebuild_working_set(true);
910✔
334
  }
335

336
  Context::getContext().time_gc_us += timer.total_us();
913✔
337
}
913✔
338

339
////////////////////////////////////////////////////////////////////////////////
340
void TDB2::expire_tasks() { replica()->expire_tasks(); }
1✔
341

342
////////////////////////////////////////////////////////////////////////////////
343
// Latest ID is that of the last pending task.
344
int TDB2::latest_id() {
167✔
345
  auto& ws = working_set();
167✔
346
  return (int)ws->largest_index();
167✔
347
}
348

349
////////////////////////////////////////////////////////////////////////////////
350
const std::vector<Task> TDB2::all_tasks() {
443✔
351
  Timer timer;
443✔
352
  auto all_tctasks = replica()->all_task_data();
443✔
353
  std::vector<Task> all;
443✔
354
  for (auto& maybe_tctask : all_tctasks) {
1,637✔
355
    auto tctask = maybe_tctask.take();
1,194✔
356
    all.push_back(Task(std::move(tctask)));
1,194✔
357
  }
1,194✔
358

359
  dependency_scan(all);
443✔
360

361
  Context::getContext().time_load_us += timer.total_us();
443✔
362
  return all;
886✔
363
}
443✔
364

365
////////////////////////////////////////////////////////////////////////////////
366
const std::vector<Task> TDB2::pending_tasks() {
6,376✔
367
  if (!_pending_tasks) {
6,376✔
368
    Timer timer;
3,784✔
369
    auto& ws = working_set();
3,784✔
370
    auto largest_index = ws->largest_index();
3,784✔
371

372
    std::vector<Task> result;
3,784✔
373
    for (size_t i = 0; i <= largest_index; i++) {
752,893✔
374
      auto uuid = ws->by_index(i);
749,109✔
375
      if (!uuid.is_nil()) {
749,109✔
376
        auto maybe_task = replica()->get_task_data(uuid);
745,281✔
377
        if (maybe_task.is_some()) {
745,281✔
378
          result.push_back(Task(maybe_task.take()));
745,281✔
379
        }
380
      }
381
    }
382

383
    dependency_scan(result);
3,784✔
384

385
    Context::getContext().time_load_us += timer.total_us();
3,784✔
386
    _pending_tasks = result;
3,784✔
387
  }
3,784✔
388

389
  return *_pending_tasks;
6,376✔
390
}
391

392
////////////////////////////////////////////////////////////////////////////////
393
const std::vector<Task> TDB2::completed_tasks() {
299✔
394
  if (!_completed_tasks) {
299✔
395
    auto all_tctasks = replica()->all_task_data();
299✔
396
    auto& ws = working_set();
299✔
397

398
    std::vector<Task> result;
299✔
399
    for (auto& maybe_tctask : all_tctasks) {
2,158✔
400
      auto tctask = maybe_tctask.take();
1,859✔
401
      // if this task is _not_ in the working set, return it.
402
      if (ws->by_uuid(tctask->get_uuid()) == 0) {
1,859✔
403
        result.push_back(Task(std::move(tctask)));
226✔
404
      }
405
    }
1,859✔
406
    _completed_tasks = result;
299✔
407
  }
299✔
408
  return *_completed_tasks;
299✔
409
}
410

411
////////////////////////////////////////////////////////////////////////////////
412
void TDB2::invalidate_cached_info() {
3,554✔
413
  _pending_tasks = std::nullopt;
3,554✔
414
  _completed_tasks = std::nullopt;
3,554✔
415
  _working_set = std::nullopt;
3,554✔
416
}
3,554✔
417

418
////////////////////////////////////////////////////////////////////////////////
419
// Locate task by ID, wherever it is.
420
bool TDB2::get(int id, Task& task) {
308✔
421
  auto& ws = working_set();
308✔
422
  const auto tcuuid = ws->by_index(id);
308✔
423
  if (!tcuuid.is_nil()) {
308✔
424
    std::string uuid = static_cast<std::string>(tcuuid.to_string());
296✔
425
    // Load all pending tasks in order to get dependency data, and in particular
426
    // `task.is_blocking` and `task.is_blocked`, set correctly.
427
    std::vector<Task> pending = pending_tasks();
296✔
428
    for (auto& pending_task : pending) {
1,537✔
429
      if (pending_task.get("uuid") == uuid) {
3,074✔
430
        task = pending_task;
296✔
431
        return true;
296✔
432
      }
433
    }
434
  }
592✔
435

436
  return false;
12✔
437
}
438

439
////////////////////////////////////////////////////////////////////////////////
440
// Locate task by UUID, including by partial ID, wherever it is.
441
bool TDB2::get(const std::string& uuid, Task& task) {
2,286✔
442
  // Load all pending tasks in order to get dependency data, and in particular
443
  // `task.is_blocking` and `task.is_blocked`, set correctly.
444
  std::vector<Task> pending = pending_tasks();
2,286✔
445

446
  // try by raw uuid, if the length is right
447
  for (auto& pending_task : pending) {
735,069✔
448
    if (closeEnough(pending_task.get("uuid"), uuid, uuid.length())) {
2,200,362✔
449
      task = pending_task;
671✔
450
      return true;
671✔
451
    }
452
  }
453

454
  // Nothing to do but iterate over all tasks and check whether it's closeEnough.
455
  for (auto& maybe_tctask : replica()->all_task_data()) {
733,510✔
456
    auto tctask = maybe_tctask.take();
731,920✔
457
    auto tctask_uuid = static_cast<std::string>(tctask->get_uuid().to_string());
731,920✔
458
    if (closeEnough(tctask_uuid, uuid, uuid.length())) {
731,920✔
459
      task = Task{std::move(tctask)};
25✔
460
      return true;
25✔
461
    }
462
  }
733,560✔
463

464
  return false;
1,590✔
465
}
2,286✔
466

467
////////////////////////////////////////////////////////////////////////////////
468
// Locate task by UUID, wherever it is.
469
bool TDB2::has(const std::string& uuid) {
×
470
  Task task;
×
471
  return get(uuid, task);
×
472
}
×
473

474
////////////////////////////////////////////////////////////////////////////////
475
const std::vector<Task> TDB2::siblings(Task& task) {
8✔
476
  std::vector<Task> results;
8✔
477
  if (task.has("parent")) {
16✔
478
    std::string parent = task.get("parent");
8✔
479

480
    for (auto& i : this->pending_tasks()) {
35✔
481
      // Do not include self in results.
482
      if (i.id != task.id) {
27✔
483
        // Do not include completed or deleted tasks.
484
        if (i.getStatus() != Task::completed && i.getStatus() != Task::deleted) {
19✔
485
          // If task has the same parent, it is a sibling.
486
          if (i.has("parent") && i.get("parent") == parent) {
77✔
487
            results.push_back(i);
10✔
488
          }
489
        }
490
      }
491
    }
8✔
492
  }
8✔
493

494
  return results;
8✔
495
}
×
496

497
////////////////////////////////////////////////////////////////////////////////
498
const std::vector<Task> TDB2::children(Task& parent) {
71✔
499
  // scan _pending_ tasks for those with `parent` equal to this task
500
  std::vector<Task> results;
71✔
501
  std::string this_uuid = parent.get("uuid");
71✔
502

503
  auto& ws = working_set();
71✔
504
  size_t end_idx = ws->largest_index();
71✔
505

506
  for (size_t i = 0; i <= end_idx; i++) {
387✔
507
    auto uuid = ws->by_index(i);
316✔
508
    if (uuid.is_nil()) {
316✔
509
      continue;
302✔
510
    }
511

512
    // skip self-references
513
    if (uuid.to_string() == this_uuid) {
245✔
514
      continue;
70✔
515
    }
516

517
    auto task_opt = replica()->get_task_data(uuid);
175✔
518
    if (task_opt.is_none()) {
175✔
519
      continue;
×
520
    }
521
    auto task = task_opt.take();
175✔
522

523
    std::string parent_uuid;
175✔
524
    if (!task->get("parent", parent_uuid)) {
525✔
525
      continue;
161✔
526
    }
527

528
    if (parent_uuid == this_uuid) {
14✔
529
      results.push_back(Task(std::move(task)));
14✔
530
    }
531
  }
336✔
532
  return results;
142✔
533
}
71✔
534

535
////////////////////////////////////////////////////////////////////////////////
536
std::string TDB2::uuid(int id) {
59✔
537
  auto& ws = working_set();
59✔
538
  auto uuid = ws->by_index(id);
59✔
539
  if (uuid.is_nil()) {
59✔
540
    return "";
2✔
541
  }
542
  return static_cast<std::string>(uuid.to_string());
58✔
543
}
544

545
////////////////////////////////////////////////////////////////////////////////
546
int TDB2::id(const std::string& uuid) {
746,740✔
547
  auto& ws = working_set();
746,740✔
548
  return ws->by_uuid(tc::uuid_from_string(uuid));
746,740✔
549
}
550

551
////////////////////////////////////////////////////////////////////////////////
552
int TDB2::num_local_changes() { return (int)replica()->num_local_operations(); }
5✔
553

554
////////////////////////////////////////////////////////////////////////////////
555
int TDB2::num_reverts_possible() { return (int)replica()->num_undo_points(); }
3✔
556

557
////////////////////////////////////////////////////////////////////////////////
558
void TDB2::dump() {
×
559
  // TODO
560
}
×
561

562
////////////////////////////////////////////////////////////////////////////////
563
// For any task that has depenencies, follow the chain of dependencies until the
564
// end.  Along the way, update the Task::is_blocked and Task::is_blocking data
565
// cache.
566
static void dependency_scan(std::vector<Task>& tasks) {
4,227✔
567
  for (auto& left : tasks) {
750,702✔
568
    for (auto& dep : left.getDependencyUUIDs()) {
746,834✔
569
      for (auto& right : tasks) {
1,514✔
570
        if (right.get("uuid") == dep) {
3,016✔
571
          // GC hasn't run yet, check both tasks for their current status
572
          Task::status lstatus = left.getStatus();
353✔
573
          Task::status rstatus = right.getStatus();
353✔
574
          if (lstatus != Task::completed && lstatus != Task::deleted &&
353✔
575
              rstatus != Task::completed && rstatus != Task::deleted) {
330✔
576
            left.is_blocked = true;
321✔
577
            right.is_blocking = true;
321✔
578
          }
579

580
          // Only want to break out of the "right" loop.
581
          break;
353✔
582
        }
583
      }
584
    }
746,475✔
585
  }
586
}
4,227✔
587

588
////////////////////////////////////////////////////////////////////////////////
589
// vim: ts=2 et sw=2
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc