mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 12:48:32 +09:00
[Fix, Test] (Pipe): read a forked refusal drive's log from the offset it had instead of unlinking the file - once a case initialises the library the log is already open, so the child was writing into a deleted inode and the parent read nothing
This commit is contained in:
@@ -78,13 +78,24 @@ namespace {
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
std::string ReadLog() {
|
||||
// `from` is a byte offset, and it exists because of the fork below: the library's log file
|
||||
// is already open by the time a case runs, so the child's lines are APPENDED to it rather
|
||||
// than written to a fresh file, and only what the child appended is this drive's evidence.
|
||||
std::string ReadLog(std::streamoff from = 0) {
|
||||
std::ifstream in(g_logPath, std::ios::binary);
|
||||
if (from > 0) in.seekg(from, std::ios::beg);
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Where the library's log file currently ends. Reading from here after the child has
|
||||
// aborted gives exactly the lines that drive produced.
|
||||
std::streamoff LogEnd() {
|
||||
std::ifstream in(g_logPath, std::ios::binary | std::ios::ate);
|
||||
return in ? static_cast<std::streamoff>(in.tellg()) : std::streamoff{0};
|
||||
}
|
||||
|
||||
// A fresh applier per case, BOTH SCOPES, and it takes both because there are two: a reset
|
||||
// is a make-current and deliberately KEEPS the object records, so a fixture that wants a
|
||||
// genuinely empty applier has to say the other one as well. Every case is its own process
|
||||
@@ -110,8 +121,14 @@ namespace {
|
||||
template <class Body>
|
||||
ChildResult RunInChild(Body body) {
|
||||
ChildResult result;
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(g_logPath, ec);
|
||||
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||
// client's cases landed in the same file as the applier's: main() calls
|
||||
// MobileGL::Initialize(), so the library's log FILE* is already open on this path and
|
||||
// fork() duplicates it. Removing the path would leave the child writing into a deleted
|
||||
// inode and the parent reading an empty file - the child would still abort, and the
|
||||
// assertion on WHAT it named could never see the line. So the log's end is remembered
|
||||
// and only what the child appended is read back.
|
||||
const std::streamoff before = LogEnd();
|
||||
std::fflush(nullptr);
|
||||
const pid_t pid = ::fork();
|
||||
if (pid < 0) return result;
|
||||
@@ -122,7 +139,7 @@ namespace {
|
||||
int status = 0;
|
||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||
result.Status = status;
|
||||
result.Log = ReadLog();
|
||||
result.Log = ReadLog(before);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,13 +69,24 @@ namespace {
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
std::string ReadLog() {
|
||||
// `from` is a byte offset, and it exists because of the fork below: the library's log file
|
||||
// is already open by the time a case runs, so the child's lines are APPENDED to it rather
|
||||
// than written to a fresh file, and only what the child appended is this drive's evidence.
|
||||
std::string ReadLog(std::streamoff from = 0) {
|
||||
std::ifstream in(g_logPath, std::ios::binary);
|
||||
if (from > 0) in.seekg(from, std::ios::beg);
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Where the library's log file currently ends. Reading from here after the child has
|
||||
// aborted gives exactly the lines that drive produced.
|
||||
std::streamoff LogEnd() {
|
||||
std::ifstream in(g_logPath, std::ios::binary | std::ios::ate);
|
||||
return in ? static_cast<std::streamoff>(in.tellg()) : std::streamoff{0};
|
||||
}
|
||||
|
||||
// A fresh applier per case, BOTH SCOPES, and it takes both because there are two: a reset
|
||||
// is a make-current and deliberately KEEPS the object records, so a fixture that wants a
|
||||
// genuinely empty applier has to say the other one as well. Every case is its own process
|
||||
@@ -101,8 +112,14 @@ namespace {
|
||||
template <class Body>
|
||||
ChildResult RunInChild(Body body) {
|
||||
ChildResult result;
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(g_logPath, ec);
|
||||
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||
// client's cases landed in the same file as the applier's: main() calls
|
||||
// MobileGL::Initialize(), so the library's log FILE* is already open on this path and
|
||||
// fork() duplicates it. Removing the path would leave the child writing into a deleted
|
||||
// inode and the parent reading an empty file - the child would still abort, and the
|
||||
// assertion on WHAT it named could never see the line. So the log's end is remembered
|
||||
// and only what the child appended is read back.
|
||||
const std::streamoff before = LogEnd();
|
||||
std::fflush(nullptr);
|
||||
const pid_t pid = ::fork();
|
||||
if (pid < 0) return result;
|
||||
@@ -113,7 +130,7 @@ namespace {
|
||||
int status = 0;
|
||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||
result.Status = status;
|
||||
result.Log = ReadLog();
|
||||
result.Log = ReadLog(before);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,13 +77,24 @@ namespace {
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
std::string ReadLog() {
|
||||
// `from` is a byte offset, and it exists because of the fork below: the library's log file
|
||||
// is already open by the time a case runs, so the child's lines are APPENDED to it rather
|
||||
// than written to a fresh file, and only what the child appended is this drive's evidence.
|
||||
std::string ReadLog(std::streamoff from = 0) {
|
||||
std::ifstream in(g_logPath, std::ios::binary);
|
||||
if (from > 0) in.seekg(from, std::ios::beg);
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Where the library's log file currently ends. Reading from here after the child has
|
||||
// aborted gives exactly the lines that drive produced.
|
||||
std::streamoff LogEnd() {
|
||||
std::ifstream in(g_logPath, std::ios::binary | std::ios::ate);
|
||||
return in ? static_cast<std::streamoff>(in.tellg()) : std::streamoff{0};
|
||||
}
|
||||
|
||||
// A fresh applier per case, BOTH SCOPES, and it takes both because there are two: a reset
|
||||
// is a make-current and deliberately KEEPS the object records, so a fixture that wants a
|
||||
// genuinely empty applier has to say the other one as well. Every case is its own process
|
||||
@@ -109,8 +120,14 @@ namespace {
|
||||
template <class Body>
|
||||
ChildResult RunInChild(Body body) {
|
||||
ChildResult result;
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(g_logPath, ec);
|
||||
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||
// client's cases landed in the same file as the applier's: main() calls
|
||||
// MobileGL::Initialize(), so the library's log FILE* is already open on this path and
|
||||
// fork() duplicates it. Removing the path would leave the child writing into a deleted
|
||||
// inode and the parent reading an empty file - the child would still abort, and the
|
||||
// assertion on WHAT it named could never see the line. So the log's end is remembered
|
||||
// and only what the child appended is read back.
|
||||
const std::streamoff before = LogEnd();
|
||||
std::fflush(nullptr);
|
||||
const pid_t pid = ::fork();
|
||||
if (pid < 0) return result;
|
||||
@@ -121,7 +138,7 @@ namespace {
|
||||
int status = 0;
|
||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||
result.Status = status;
|
||||
result.Log = ReadLog();
|
||||
result.Log = ReadLog(before);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,13 +80,24 @@ namespace {
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
std::string ReadLog() {
|
||||
// `from` is a byte offset, and it exists because of the fork below: the library's log file
|
||||
// is already open by the time a case runs, so the child's lines are APPENDED to it rather
|
||||
// than written to a fresh file, and only what the child appended is this drive's evidence.
|
||||
std::string ReadLog(std::streamoff from = 0) {
|
||||
std::ifstream in(g_logPath, std::ios::binary);
|
||||
if (from > 0) in.seekg(from, std::ios::beg);
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Where the library's log file currently ends. Reading from here after the child has
|
||||
// aborted gives exactly the lines that drive produced.
|
||||
std::streamoff LogEnd() {
|
||||
std::ifstream in(g_logPath, std::ios::binary | std::ios::ate);
|
||||
return in ? static_cast<std::streamoff>(in.tellg()) : std::streamoff{0};
|
||||
}
|
||||
|
||||
// A fresh applier per case, BOTH SCOPES, and it takes both because there are two: a reset
|
||||
// is a make-current and deliberately KEEPS the object records, so a fixture that wants a
|
||||
// genuinely empty applier has to say the other one as well. Every case is its own process
|
||||
@@ -112,8 +123,14 @@ namespace {
|
||||
template <class Body>
|
||||
ChildResult RunInChild(Body body) {
|
||||
ChildResult result;
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(g_logPath, ec);
|
||||
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||
// client's cases landed in the same file as the applier's: main() calls
|
||||
// MobileGL::Initialize(), so the library's log FILE* is already open on this path and
|
||||
// fork() duplicates it. Removing the path would leave the child writing into a deleted
|
||||
// inode and the parent reading an empty file - the child would still abort, and the
|
||||
// assertion on WHAT it named could never see the line. So the log's end is remembered
|
||||
// and only what the child appended is read back.
|
||||
const std::streamoff before = LogEnd();
|
||||
std::fflush(nullptr);
|
||||
const pid_t pid = ::fork();
|
||||
if (pid < 0) return result;
|
||||
@@ -124,7 +141,7 @@ namespace {
|
||||
int status = 0;
|
||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||
result.Status = status;
|
||||
result.Log = ReadLog();
|
||||
result.Log = ReadLog(before);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user