mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +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
|
#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);
|
std::ifstream in(g_logPath, std::ios::binary);
|
||||||
|
if (from > 0) in.seekg(from, std::ios::beg);
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << in.rdbuf();
|
ss << in.rdbuf();
|
||||||
return ss.str();
|
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
|
// 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
|
// 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
|
// 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>
|
template <class Body>
|
||||||
ChildResult RunInChild(Body body) {
|
ChildResult RunInChild(Body body) {
|
||||||
ChildResult result;
|
ChildResult result;
|
||||||
std::error_code ec;
|
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||||
std::filesystem::remove(g_logPath, ec);
|
// 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);
|
std::fflush(nullptr);
|
||||||
const pid_t pid = ::fork();
|
const pid_t pid = ::fork();
|
||||||
if (pid < 0) return result;
|
if (pid < 0) return result;
|
||||||
@@ -122,7 +139,7 @@ namespace {
|
|||||||
int status = 0;
|
int status = 0;
|
||||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||||
result.Status = status;
|
result.Status = status;
|
||||||
result.Log = ReadLog();
|
result.Log = ReadLog(before);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,13 +69,24 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MOBILEGL_PIPE_PUSH
|
#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);
|
std::ifstream in(g_logPath, std::ios::binary);
|
||||||
|
if (from > 0) in.seekg(from, std::ios::beg);
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << in.rdbuf();
|
ss << in.rdbuf();
|
||||||
return ss.str();
|
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
|
// 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
|
// 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
|
// 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>
|
template <class Body>
|
||||||
ChildResult RunInChild(Body body) {
|
ChildResult RunInChild(Body body) {
|
||||||
ChildResult result;
|
ChildResult result;
|
||||||
std::error_code ec;
|
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||||
std::filesystem::remove(g_logPath, ec);
|
// 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);
|
std::fflush(nullptr);
|
||||||
const pid_t pid = ::fork();
|
const pid_t pid = ::fork();
|
||||||
if (pid < 0) return result;
|
if (pid < 0) return result;
|
||||||
@@ -113,7 +130,7 @@ namespace {
|
|||||||
int status = 0;
|
int status = 0;
|
||||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||||
result.Status = status;
|
result.Status = status;
|
||||||
result.Log = ReadLog();
|
result.Log = ReadLog(before);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,13 +77,24 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MOBILEGL_PIPE_PUSH
|
#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);
|
std::ifstream in(g_logPath, std::ios::binary);
|
||||||
|
if (from > 0) in.seekg(from, std::ios::beg);
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << in.rdbuf();
|
ss << in.rdbuf();
|
||||||
return ss.str();
|
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
|
// 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
|
// 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
|
// 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>
|
template <class Body>
|
||||||
ChildResult RunInChild(Body body) {
|
ChildResult RunInChild(Body body) {
|
||||||
ChildResult result;
|
ChildResult result;
|
||||||
std::error_code ec;
|
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||||
std::filesystem::remove(g_logPath, ec);
|
// 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);
|
std::fflush(nullptr);
|
||||||
const pid_t pid = ::fork();
|
const pid_t pid = ::fork();
|
||||||
if (pid < 0) return result;
|
if (pid < 0) return result;
|
||||||
@@ -121,7 +138,7 @@ namespace {
|
|||||||
int status = 0;
|
int status = 0;
|
||||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||||
result.Status = status;
|
result.Status = status;
|
||||||
result.Log = ReadLog();
|
result.Log = ReadLog(before);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,13 +80,24 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MOBILEGL_PIPE_PUSH
|
#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);
|
std::ifstream in(g_logPath, std::ios::binary);
|
||||||
|
if (from > 0) in.seekg(from, std::ios::beg);
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << in.rdbuf();
|
ss << in.rdbuf();
|
||||||
return ss.str();
|
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
|
// 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
|
// 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
|
// 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>
|
template <class Body>
|
||||||
ChildResult RunInChild(Body body) {
|
ChildResult RunInChild(Body body) {
|
||||||
ChildResult result;
|
ChildResult result;
|
||||||
std::error_code ec;
|
// THE LOG PATH IS NOT UNLINKED HERE, and that is what this helper had to learn when the
|
||||||
std::filesystem::remove(g_logPath, ec);
|
// 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);
|
std::fflush(nullptr);
|
||||||
const pid_t pid = ::fork();
|
const pid_t pid = ::fork();
|
||||||
if (pid < 0) return result;
|
if (pid < 0) return result;
|
||||||
@@ -124,7 +141,7 @@ namespace {
|
|||||||
int status = 0;
|
int status = 0;
|
||||||
if (::waitpid(pid, &status, 0) != pid) return result;
|
if (::waitpid(pid, &status, 0) != pid) return result;
|
||||||
result.Status = status;
|
result.Status = status;
|
||||||
result.Log = ReadLog();
|
result.Log = ReadLog(before);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user