aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/error.cc246
-rw-r--r--src/libutil/error.hh86
-rw-r--r--src/libutil/logging.cc9
-rw-r--r--src/libutil/logging.hh11
-rw-r--r--src/libutil/tests/logging.cc115
5 files changed, 357 insertions, 110 deletions
diff --git a/src/libutil/error.cc b/src/libutil/error.cc
index 0fad9ae42..fd6f69b7f 100644
--- a/src/libutil/error.cc
+++ b/src/libutil/error.cc
@@ -7,14 +7,11 @@
namespace nix {
-
const std::string nativeSystem = SYSTEM;
-// addPrefix is used for show-trace. Strings added with addPrefix
-// will print ahead of the error itself.
-BaseError & BaseError::addPrefix(const FormatOrString & fs)
+BaseError & BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
{
- prefix_ = fs.s + prefix_;
+ err.traces.push_front(Trace { .pos = e, .hint = hint});
return *this;
}
@@ -28,7 +25,7 @@ const string& BaseError::calcWhat() const
err.name = sname();
std::ostringstream oss;
- oss << err;
+ showErrorInfo(oss, err, false);
what_ = oss.str();
return *what_;
@@ -56,28 +53,114 @@ string showErrPos(const ErrPos &errPos)
}
}
-// if nixCode contains lines of code, print them to the ostream, indicating the error column.
-void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixCode)
+std::optional<LinesOfCode> getCodeLines(const ErrPos &errPos)
+{
+ if (errPos.line <= 0)
+ return std::nullopt;
+
+ if (errPos.origin == foFile) {
+ LinesOfCode loc;
+ try {
+ AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC);
+ if (!fd) {
+ logError(SysError("opening file '%1%'", errPos.file).info());
+ return std::nullopt;
+ }
+ else
+ {
+ // count the newlines.
+ int count = 0;
+ string line;
+ int pl = errPos.line - 1;
+ do
+ {
+ line = readLine(fd.get());
+ ++count;
+ if (count < pl)
+ {
+ ;
+ }
+ else if (count == pl) {
+ loc.prevLineOfCode = line;
+ } else if (count == pl + 1) {
+ loc.errLineOfCode = line;
+ } else if (count == pl + 2) {
+ loc.nextLineOfCode = line;
+ break;
+ }
+ } while (true);
+ return loc;
+ }
+ }
+ catch (EndOfFile &eof) {
+ if (loc.errLineOfCode.has_value())
+ return loc;
+ else
+ return std::nullopt;
+ }
+ catch (std::exception &e) {
+ printError("error reading nix file: %s\n%s", errPos.file, e.what());
+ return std::nullopt;
+ }
+ } else {
+ std::istringstream iss(errPos.file);
+ // count the newlines.
+ int count = 0;
+ string line;
+ int pl = errPos.line - 1;
+
+ LinesOfCode loc;
+
+ do
+ {
+ std::getline(iss, line);
+ ++count;
+ if (count < pl)
+ {
+ ;
+ }
+ else if (count == pl) {
+ loc.prevLineOfCode = line;
+ } else if (count == pl + 1) {
+ loc.errLineOfCode = line;
+ } else if (count == pl + 2) {
+ loc.nextLineOfCode = line;
+ break;
+ }
+
+ if (!iss.good())
+ break;
+ } while (true);
+
+ return loc;
+ }
+}
+
+// print lines of code to the ostream, indicating the error column.
+void printCodeLines(std::ostream &out,
+ const string &prefix,
+ const ErrPos &errPos,
+ const LinesOfCode &loc)
{
// previous line of code.
- if (nixCode.prevLineOfCode.has_value()) {
+ if (loc.prevLineOfCode.has_value()) {
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.line - 1),
- *nixCode.prevLineOfCode);
+ prefix,
+ (errPos.line - 1),
+ *loc.prevLineOfCode);
}
- if (nixCode.errLineOfCode.has_value()) {
+ if (loc.errLineOfCode.has_value()) {
// line of code containing the error.
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.line),
- *nixCode.errLineOfCode);
+ prefix,
+ (errPos.line),
+ *loc.errLineOfCode);
// error arrows for the column range.
- if (nixCode.errPos.column > 0) {
- int start = nixCode.errPos.column;
+ if (errPos.column > 0) {
+ int start = errPos.column;
std::string spaces;
for (int i = 0; i < start; ++i) {
spaces.append(" ");
@@ -87,23 +170,49 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
out << std::endl
<< fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
- prefix,
- spaces,
- arrows);
+ prefix,
+ spaces,
+ arrows);
}
}
// next line of code.
- if (nixCode.nextLineOfCode.has_value()) {
+ if (loc.nextLineOfCode.has_value()) {
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.line + 1),
- *nixCode.nextLineOfCode);
+ prefix,
+ (errPos.line + 1),
+ *loc.nextLineOfCode);
}
}
-std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
+void printAtPos(const string &prefix, const ErrPos &pos, std::ostream &out)
+{
+ if (pos)
+ {
+ switch (pos.origin) {
+ case foFile: {
+ out << prefix << ANSI_BLUE << "at: " << ANSI_YELLOW << showErrPos(pos) <<
+ ANSI_BLUE << " in file: " << ANSI_NORMAL << pos.file;
+ break;
+ }
+ case foString: {
+ out << prefix << ANSI_BLUE << "at: " << ANSI_YELLOW << showErrPos(pos) <<
+ ANSI_BLUE << " from string" << ANSI_NORMAL;
+ break;
+ }
+ case foStdin: {
+ out << prefix << ANSI_BLUE << "at: " << ANSI_YELLOW << showErrPos(pos) <<
+ ANSI_BLUE << " from stdin" << ANSI_NORMAL;
+ break;
+ }
+ default:
+ throw Error("invalid FileOrigin in errPos");
+ }
+ }
+}
+
+std::ostream& showErrorInfo(std::ostream &out, const ErrorInfo &einfo, bool showTrace)
{
auto errwidth = std::max<size_t>(getWindowSize().second, 20);
string prefix = "";
@@ -158,8 +267,12 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
}
}
- auto ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
- auto dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
+ auto ndl = prefix.length()
+ + filterANSIEscapes(levelString, true).length()
+ + 7
+ + einfo.name.length()
+ + einfo.programName.value_or("").length();
+ auto dashwidth = std::max<int>(errwidth - ndl, 3);
std::string dashes(dashwidth, '-');
@@ -179,16 +292,9 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
einfo.programName.value_or(""));
bool nl = false; // intersperse newline between sections.
- if (einfo.nixCode.has_value()) {
- if (einfo.nixCode->errPos.file != "") {
- // filename, line, column.
- out << std::endl << fmt("%1%in file: " ANSI_BLUE "%2% %3%" ANSI_NORMAL,
- prefix,
- einfo.nixCode->errPos.file,
- showErrPos(einfo.nixCode->errPos));
- } else {
- out << std::endl << fmt("%1%from command line argument", prefix);
- }
+ if (einfo.errPos.has_value() && (*einfo.errPos)) {
+ out << prefix << std::endl;
+ printAtPos(prefix, *einfo.errPos, out);
nl = true;
}
@@ -200,12 +306,16 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
nl = true;
}
- // lines of code.
- if (einfo.nixCode.has_value() && einfo.nixCode->errLineOfCode.has_value()) {
- if (nl)
- out << std::endl << prefix;
- printCodeLines(out, prefix, *einfo.nixCode);
- nl = true;
+ if (einfo.errPos.has_value() && (*einfo.errPos)) {
+ auto loc = getCodeLines(*einfo.errPos);
+
+ // lines of code.
+ if (loc.has_value()) {
+ if (nl)
+ out << std::endl << prefix;
+ printCodeLines(out, prefix, *einfo.errPos, *loc);
+ nl = true;
+ }
}
// hint
@@ -216,6 +326,54 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
nl = true;
}
+ // traces
+ if (showTrace && !einfo.traces.empty())
+ {
+ const string tracetitle(" show-trace ");
+
+ int fill = errwidth - tracetitle.length();
+ int lw = 0;
+ int rw = 0;
+ const int min_dashes = 3;
+ if (fill > min_dashes * 2) {
+ if (fill % 2 != 0) {
+ lw = fill / 2;
+ rw = lw + 1;
+ }
+ else
+ {
+ lw = rw = fill / 2;
+ }
+ }
+ else
+ lw = rw = min_dashes;
+
+ if (nl)
+ out << std::endl << prefix;
+
+ out << ANSI_BLUE << std::string(lw, '-') << tracetitle << std::string(rw, '-') << ANSI_NORMAL;
+
+ for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter)
+ {
+ out << std::endl << prefix;
+ out << ANSI_BLUE << "trace: " << ANSI_NORMAL << iter->hint.str();
+
+ if (iter->pos.has_value() && (*iter->pos)) {
+ auto pos = iter->pos.value();
+ out << std::endl << prefix;
+ printAtPos(prefix, pos, out);
+
+ auto loc = getCodeLines(pos);
+ if (loc.has_value())
+ {
+ out << std::endl << prefix;
+ printCodeLines(out, prefix, pos, *loc);
+ out << std::endl << prefix;
+ }
+ }
+ }
+ }
+
return out;
}
}
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
index 6982e30aa..f4b3f11bb 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -1,8 +1,8 @@
#pragma once
-
#include "ref.hh"
#include "types.hh"
+#include "fmt.hh"
#include <cstring>
#include <list>
@@ -10,7 +10,9 @@
#include <map>
#include <optional>
-#include "fmt.hh"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
* its (virtual) destructor and what() in c++11 mode, in violation of spec
@@ -25,20 +27,20 @@ namespace nix {
/*
-This file defines two main structs/classes used in nix error handling.
+ This file defines two main structs/classes used in nix error handling.
-ErrorInfo provides a standard payload of error information, with conversion to string
-happening in the logger rather than at the call site.
+ ErrorInfo provides a standard payload of error information, with conversion to string
+ happening in the logger rather than at the call site.
-BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
-an ErrorInfo.
+ BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
+ an ErrorInfo.
-ErrorInfo structs are sent to the logger as part of an exception, or directly with the
-logError or logWarning macros.
+ ErrorInfo structs are sent to the logger as part of an exception, or directly with the
+ logError or logWarning macros.
-See the error-demo.cc program for usage examples.
+ See the error-demo.cc program for usage examples.
-*/
+ */
typedef enum {
lvlError = 0,
@@ -50,11 +52,25 @@ typedef enum {
lvlVomit
} Verbosity;
+typedef enum {
+ foFile,
+ foStdin,
+ foString
+} FileOrigin;
+
+// the lines of code surrounding an error.
+struct LinesOfCode {
+ std::optional<string> prevLineOfCode;
+ std::optional<string> errLineOfCode;
+ std::optional<string> nextLineOfCode;
+};
+
// ErrPos indicates the location of an error in a nix file.
struct ErrPos {
int line = 0;
int column = 0;
string file;
+ FileOrigin origin;
operator bool() const
{
@@ -65,6 +81,7 @@ struct ErrPos {
template <class P>
ErrPos& operator=(const P &pos)
{
+ origin = pos.origin;
line = pos.line;
column = pos.column;
// is file symbol null?
@@ -82,11 +99,9 @@ struct ErrPos {
}
};
-struct NixCode {
- ErrPos errPos;
- std::optional<string> prevLineOfCode;
- std::optional<string> errLineOfCode;
- std::optional<string> nextLineOfCode;
+struct Trace {
+ std::optional<ErrPos> pos;
+ hintformat hint;
};
struct ErrorInfo {
@@ -94,19 +109,19 @@ struct ErrorInfo {
string name;
string description;
std::optional<hintformat> hint;
- std::optional<NixCode> nixCode;
+ std::optional<ErrPos> errPos;
+ std::list<Trace> traces;
static std::optional<string> programName;
};
-std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo);
+std::ostream& showErrorInfo(std::ostream &out, const ErrorInfo &einfo, bool showTrace);
/* BaseError should generally not be caught, as it has Interrupted as
a subclass. Catch Error instead. */
class BaseError : public std::exception
{
protected:
- string prefix_; // used for location traces etc.
mutable ErrorInfo err;
mutable std::optional<string> what_;
@@ -117,23 +132,23 @@ public:
template<typename... Args>
BaseError(unsigned int status, const Args & ... args)
- : err { .level = lvlError,
- .hint = hintfmt(args...)
- }
+ : err {.level = lvlError,
+ .hint = hintfmt(args...)
+ }
, status(status)
{ }
template<typename... Args>
BaseError(const std::string & fs, const Args & ... args)
- : err { .level = lvlError,
- .hint = hintfmt(fs, args...)
- }
+ : err {.level = lvlError,
+ .hint = hintfmt(fs, args...)
+ }
{ }
BaseError(hintformat hint)
- : err { .level = lvlError,
- .hint = hint
- }
+ : err {.level = lvlError,
+ .hint = hint
+ }
{ }
BaseError(ErrorInfo && e)
@@ -154,10 +169,17 @@ public:
#endif
const string & msg() const { return calcWhat(); }
- const string & prefix() const { return prefix_; }
- BaseError & addPrefix(const FormatOrString & fs);
-
const ErrorInfo & info() { calcWhat(); return err; }
+
+ template<typename... Args>
+ BaseError & addTrace(std::optional<ErrPos> e, const string &fs, const Args & ... args)
+ {
+ return addTrace(e, hintfmt(fs, args...));
+ }
+
+ BaseError & addTrace(std::optional<ErrPos> e, hintformat hint);
+
+ bool hasTrace() const { return !err.traces.empty(); }
};
#define MakeError(newClass, superClass) \
@@ -177,7 +199,7 @@ public:
template<typename... Args>
SysError(const Args & ... args)
- :Error("")
+ : Error("")
{
errNo = errno;
auto hf = hintfmt(args...);
diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc
index 105fadb15..832aee783 100644
--- a/src/libutil/logging.cc
+++ b/src/libutil/logging.cc
@@ -1,5 +1,6 @@
#include "logging.hh"
#include "util.hh"
+#include "config.hh"
#include <atomic>
#include <nlohmann/json.hpp>
@@ -7,6 +8,10 @@
namespace nix {
+LoggerSettings loggerSettings;
+
+static GlobalConfig::Register r1(&loggerSettings);
+
static thread_local ActivityId curActivity = 0;
ActivityId getCurActivity()
@@ -72,7 +77,7 @@ public:
void logEI(const ErrorInfo & ei) override
{
std::stringstream oss;
- oss << ei;
+ showErrorInfo(oss, ei, loggerSettings.showTrace.get());
log(ei.level, oss.str());
}
@@ -173,7 +178,7 @@ struct JSONLogger : Logger {
void logEI(const ErrorInfo & ei) override
{
std::ostringstream oss;
- oss << ei;
+ showErrorInfo(oss, ei, loggerSettings.showTrace.get());
nlohmann::json json;
json["action"] = "msg";
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index b1583eced..09619aac6 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -2,6 +2,7 @@
#include "types.hh"
#include "error.hh"
+#include "config.hh"
namespace nix {
@@ -34,6 +35,16 @@ typedef enum {
typedef uint64_t ActivityId;
+struct LoggerSettings : Config
+{
+ Setting<bool> showTrace{this,
+ false,
+ "show-trace",
+ "Whether to show a stack trace on evaluation errors."};
+};
+
+extern LoggerSettings loggerSettings;
+
class Logger
{
friend struct Activity;
diff --git a/src/libutil/tests/logging.cc b/src/libutil/tests/logging.cc
index 6a58b9425..ad588055f 100644
--- a/src/libutil/tests/logging.cc
+++ b/src/libutil/tests/logging.cc
@@ -11,6 +11,13 @@ namespace nix {
* logEI
* --------------------------------------------------------------------------*/
+ const char *test_file =
+ "previous line of code\n"
+ "this is the problem line of code\n"
+ "next line of code\n";
+ const char *one_liner =
+ "this is the other problem line of code";
+
TEST(logEI, catpuresBasicProperties) {
MakeError(TestError, Error);
@@ -137,7 +144,6 @@ namespace nix {
* logError
* --------------------------------------------------------------------------*/
-
TEST(logError, logErrorWithoutHintOrCode) {
testing::internal::CaptureStderr();
@@ -152,7 +158,7 @@ namespace nix {
TEST(logError, logErrorWithPreviousAndNextLinesOfCode) {
SymbolTable testTable;
- auto problem_file = testTable.create("myfile.nix");
+ auto problem_file = testTable.create(test_file);
testing::internal::CaptureStderr();
@@ -162,21 +168,16 @@ namespace nix {
.hint = hintfmt("this hint has %1% templated %2%!!",
"yellow",
"values"),
- .nixCode = NixCode {
- .errPos = Pos(problem_file, 40, 13),
- .prevLineOfCode = "previous line of code",
- .errLineOfCode = "this is the problem line of code",
- .nextLineOfCode = "next line of code",
- }});
-
+ .errPos = Pos(foString, problem_file, 02, 13),
+ });
auto str = testing::internal::GetCapturedStderr();
- ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror with code lines\n\n 39| previous line of code\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 41| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nerror with code lines\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
}
- TEST(logError, logErrorWithoutLinesOfCode) {
+ TEST(logError, logErrorWithInvalidFile) {
SymbolTable testTable;
- auto problem_file = testTable.create("myfile.nix");
+ auto problem_file = testTable.create("invalid filename");
testing::internal::CaptureStderr();
logError({
@@ -185,28 +186,23 @@ namespace nix {
.hint = hintfmt("this hint has %1% templated %2%!!",
"yellow",
"values"),
- .nixCode = NixCode {
- .errPos = Pos(problem_file, 40, 13)
- }});
+ .errPos = Pos(foFile, problem_file, 02, 13)
+ });
auto str = testing::internal::GetCapturedStderr();
- ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nopening file '\x1B[33;1minvalid filename\x1B[0m': \x1B[33;1mNo such file or directory\x1B[0m\n\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m in file: \x1B[0minvalid filename\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
}
TEST(logError, logErrorWithOnlyHintAndName) {
- SymbolTable testTable;
- auto problem_file = testTable.create("myfile.nix");
testing::internal::CaptureStderr();
logError({
.name = "error name",
.hint = hintfmt("hint %1%", "only"),
- .nixCode = NixCode {
- .errPos = Pos(problem_file, 40, 13)
- }});
+ });
auto str = testing::internal::GetCapturedStderr();
- ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nhint \x1B[33;1monly\x1B[0m\n");
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nhint \x1B[33;1monly\x1B[0m\n");
}
@@ -219,18 +215,18 @@ namespace nix {
logWarning({
.name = "name",
- .description = "error description",
+ .description = "warning description",
.hint = hintfmt("there was a %1%", "warning"),
});
auto str = testing::internal::GetCapturedStderr();
- ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nwarning description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
}
TEST(logWarning, logWarningWithFileLineNumAndCode) {
SymbolTable testTable;
- auto problem_file = testTable.create("myfile.nix");
+ auto problem_file = testTable.create(test_file);
testing::internal::CaptureStderr();
@@ -240,18 +236,73 @@ namespace nix {
.hint = hintfmt("this hint has %1% templated %2%!!",
"yellow",
"values"),
- .nixCode = NixCode {
- .errPos = Pos(problem_file, 40, 13),
- .prevLineOfCode = std::nullopt,
- .errLineOfCode = "this is the problem line of code",
- .nextLineOfCode = std::nullopt
- }});
+ .errPos = Pos(foStdin, problem_file, 2, 13),
+ });
+
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from stdin\x1B[0m\n\nwarning description\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * traces
+ * --------------------------------------------------------------------------*/
+
+ TEST(addTrace, showTracesWithShowTrace) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+ auto oneliner_file = testTable.create(one_liner);
+ auto invalidfilename = testTable.create("invalid filename");
+
+ auto e = AssertionError(ErrorInfo {
+ .name = "wat",
+ .description = "show-traces",
+ .hint = hintfmt("it has been %1% days since our last error", "zero"),
+ .errPos = Pos(foString, problem_file, 2, 13),
+ });
+
+ e.addTrace(Pos(foStdin, oneliner_file, 1, 19), "while trying to compute %1%", 42);
+ e.addTrace(std::nullopt, "while doing something without a %1%", "pos");
+ e.addTrace(Pos(foFile, invalidfilename, 100, 1), "missing %s", "nix file");
+
+ testing::internal::CaptureStderr();
+
+ loggerSettings.showTrace.assign(true);
+
+ logError(e.info());
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nopening file '\x1B[33;1minvalid filename\x1B[0m': \x1B[33;1mNo such file or directory\x1B[0m\n\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nshow-traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n\x1B[34;1m---- show-trace ----\x1B[0m\n\x1B[34;1mtrace: \x1B[0mwhile trying to compute \x1B[33;1m42\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(1:19)\x1B[34;1m from stdin\x1B[0m\n\n 1| this is the other problem line of code\n | \x1B[31;1m^\x1B[0m\n\n\x1B[34;1mtrace: \x1B[0mwhile doing something without a \x1B[33;1mpos\x1B[0m\n\x1B[34;1mtrace: \x1B[0mmissing \x1B[33;1mnix file\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(100:1)\x1B[34;1m in file: \x1B[0minvalid filename\n");
+ }
+ TEST(addTrace, hideTracesWithoutShowTrace) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+ auto oneliner_file = testTable.create(one_liner);
+ auto invalidfilename = testTable.create("invalid filename");
+
+ auto e = AssertionError(ErrorInfo {
+ .name = "wat",
+ .description = "hide traces",
+ .hint = hintfmt("it has been %1% days since our last error", "zero"),
+ .errPos = Pos(foString, problem_file, 2, 13),
+ });
+
+ e.addTrace(Pos(foStdin, oneliner_file, 1, 19), "while trying to compute %1%", 42);
+ e.addTrace(std::nullopt, "while doing something without a %1%", "pos");
+ e.addTrace(Pos(foFile, invalidfilename, 100, 1), "missing %s", "nix file");
+
+ testing::internal::CaptureStderr();
+
+ loggerSettings.showTrace.assign(false);
+
+ logError(e.info());
auto str = testing::internal::GetCapturedStderr();
- ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nwarning description\n\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nhide traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n");
}
+
/* ----------------------------------------------------------------------------
* hintfmt
* --------------------------------------------------------------------------*/