Quelle TestEtcHostsParsing.cpp
Sprache: C
#include "gtest/gtest.h"
#include "mozilla/net/rust_helper.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "nsNetUtil.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include <fcntl.h>
#include <sys/stat.h>
#ifndef XP_WIN
# include <sys/file.h>
# include <unistd.h>
#endif
#ifdef XP_WIN
# include <io.h>
# include <share.h>
#endif
using namespace mozilla;
using namespace mozilla::net;
class TestEtcHostsParsing : public ::testing::Test {
protected :
void SetUp() override {
// Get temp directory
nsCOMPtr<nsIFile> tmpDir;
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Create unique test file names
nsAutoString baseName(u"test_hosts_" );
baseName.AppendInt(PR_IntervalNow());
// Normal test file
rv = tmpDir->Clone(getter_AddRefs(mTestHostsFile));
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsAutoString normalName = baseName + u"_normal" _ns;
rv = mTestHostsFile->Append(normalName);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Locked test file
rv = tmpDir->Clone(getter_AddRefs(mLockedHostsFile));
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsAutoString lockedName = baseName + u"_locked" _ns;
rv = mLockedHostsFile->Append(lockedName);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Permission denied test file
rv = tmpDir->Clone(getter_AddRefs(mNoPermHostsFile));
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsAutoString noPermName = baseName + u"_noperm" _ns;
rv = mNoPermHostsFile->Append(noPermName);
ASSERT_TRUE(NS_SUCCEEDED(rv));
mCallbackInvoked = false ;
mCallbackResult = true ;
mParsedHosts.Clear();
}
void TearDown() override {
// Clean up test files
if (mTestHostsFile) {
mTestHostsFile->Remove(false );
}
if (mLockedHostsFile) {
mLockedHostsFile->Remove(false );
}
if (mNoPermHostsFile) {
mNoPermHostsFile->Remove(false );
}
// Close any open file descriptors
if (mLockFd != -1 ) {
#ifdef XP_WIN
_close(mLockFd);
#else
close(mLockFd);
#endif
mLockFd = -1 ;
}
}
void CreateNormalHostsFile() {
nsCOMPtr<nsIOutputStream> stream;
nsresult rv =
NS_NewLocalFileOutputStream(getter_AddRefs(stream), mTestHostsFile);
ASSERT_TRUE(NS_SUCCEEDED(rv));
const char * content =
"127.0.0.1 localhost\n"
"::1 localhost\n"
"127.0.0.1 example.com www.example.com\n"
"# This is a comment\n"
"192.168.1.1 router.local\n" ;
uint32_t written;
rv = stream->Write(content, strlen(content), &written);
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_EQ(written, strlen(content));
rv = stream->Close();
ASSERT_TRUE(NS_SUCCEEDED(rv));
}
void CreateLockedHostsFile() {
// First create the file with content
nsCOMPtr<nsIOutputStream> stream;
nsresult rv =
NS_NewLocalFileOutputStream(getter_AddRefs(stream), mLockedHostsFile);
ASSERT_TRUE(NS_SUCCEEDED(rv));
const char * content = "127.0.0.1 locked.test\n" ;
uint32_t written;
rv = stream->Write(content, strlen(content), &written);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rv = stream->Close();
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Now lock it by opening with exclusive access
nsAutoCString nativePath;
rv = GetCrossplatformNativePath(mLockedHostsFile, nativePath);
ASSERT_TRUE(NS_SUCCEEDED(rv));
#ifdef XP_WIN
// On Windows, open with no sharing to simulate a locked file
mLockFd = _sopen(nativePath.get(), _O_RDONLY, _SH_DENYRW, _S_IREAD);
#else
// On Unix, use flock to lock the file
mLockFd = open(nativePath.get(), O_RDONLY);
if (mLockFd != -1 ) {
::flock(mLockFd, LOCK_EX | LOCK_NB); // Exclusive, non-blocking lock
}
#endif
ASSERT_NE(mLockFd, -1 );
}
void CreateNoPermissionFile() {
#ifndef XP_WIN
// Create the file first
nsCOMPtr<nsIOutputStream> stream;
nsresult rv =
NS_NewLocalFileOutputStream(getter_AddRefs(stream), mNoPermHostsFile);
ASSERT_TRUE(NS_SUCCEEDED(rv));
const char * content = "127.0.0.1 noperm.test\n" ;
uint32_t written;
rv = stream->Write(content, strlen(content), &written);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rv = stream->Close();
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Remove read permissions
nsAutoCString nativePath;
rv = GetCrossplatformNativePath(mNoPermHostsFile, nativePath);
ASSERT_TRUE(NS_SUCCEEDED(rv));
chmod(nativePath.get(), 0000 ); // No permissions
#endif
}
nsresult GetCrossplatformNativePath(nsIFile* aFile, nsACString& aPath) {
#ifdef XP_WIN
nsAutoString widePath;
nsresult rv = aFile->GetPath(widePath);
if (NS_FAILED(rv)) return rv;
aPath = NS_ConvertUTF16toUTF8(widePath);
return NS_OK;
#else
return aFile->GetNativePath(aPath);
#endif
}
static bool TestCallback(const nsTArray<nsCString>* aArray) {
auto * self = static_cast <TestEtcHostsParsing*>(sCurrentTest);
self->mCallbackInvoked = true ;
if (aArray) {
self->mParsedHosts.AppendElements(*aArray);
}
return self->mCallbackResult;
}
nsCOMPtr<nsIFile> mTestHostsFile;
nsCOMPtr<nsIFile> mLockedHostsFile;
nsCOMPtr<nsIFile> mNoPermHostsFile;
int mLockFd = -1 ;
bool mCallbackInvoked;
bool mCallbackResult;
nsTArray<nsCString> mParsedHosts;
static TestEtcHostsParsing* sCurrentTest;
};
TestEtcHostsParsing* TestEtcHostsParsing::sCurrentTest = nullptr;
TEST_F(TestEtcHostsParsing, ParseNormalFile) {
sCurrentTest = this ;
CreateNormalHostsFile();
nsAutoCString path;
nsresult rv = GetCrossplatformNativePath(mTestHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rust_parse_etc_hosts(&path, TestCallback);
EXPECT_TRUE(mCallbackInvoked);
EXPECT_GT(mParsedHosts.Length(), 0 u);
// Check that expected hostnames are parsed
bool foundLocalhost = false ;
bool foundExampleCom = false ;
bool foundWwwExampleCom = false ;
bool foundRouterLocal = false ;
for (const auto & host : mParsedHosts) {
if (host.EqualsLiteral("localhost" )) foundLocalhost = true ;
if (host.EqualsLiteral("example.com" )) foundExampleCom = true ;
if (host.EqualsLiteral("www.example.com" )) foundWwwExampleCom = true ;
if (host.EqualsLiteral("router.local" )) foundRouterLocal = true ;
}
EXPECT_TRUE(foundLocalhost);
EXPECT_TRUE(foundExampleCom);
EXPECT_TRUE(foundWwwExampleCom);
EXPECT_TRUE(foundRouterLocal);
}
TEST_F(TestEtcHostsParsing, ParseLockedFile) {
sCurrentTest = this ;
CreateLockedHostsFile();
nsAutoCString path;
nsresult rv = GetCrossplatformNativePath(mLockedHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// The function should handle locked files gracefully and not crash or hang
rust_parse_etc_hosts(&path, TestCallback);
// On some systems, locked files might still be readable, on others not.
// The important thing is that the function doesn't crash or hang.
// We don't assert on mCallbackInvoked because it depends on platform
// behavior.
}
TEST_F(TestEtcHostsParsing, ParseNoPermissionFile) {
#ifndef XP_WIN
sCurrentTest = this ;
CreateNoPermissionFile();
nsAutoCString path;
nsresult rv = GetCrossplatformNativePath(mNoPermHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// The function should handle permission denied gracefully
rust_parse_etc_hosts(&path, TestCallback);
// Callback should not be invoked for files we can't read
EXPECT_FALSE(mCallbackInvoked);
#endif
}
TEST_F(TestEtcHostsParsing, ParseNonExistentFile) {
sCurrentTest = this ;
nsAutoCString fakePath("/nonexistent/file/hosts" );
// The function should handle non-existent files gracefully
rust_parse_etc_hosts(&fakePath, TestCallback);
// Callback should not be invoked for non-existent files
EXPECT_FALSE(mCallbackInvoked);
}
TEST_F(TestEtcHostsParsing, CallbackReturnsFalse) {
sCurrentTest = this ;
CreateNormalHostsFile();
mCallbackResult = false ; // Tell callback to return false
nsAutoCString path;
nsresult rv = GetCrossplatformNativePath(mTestHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rust_parse_etc_hosts(&path, TestCallback);
EXPECT_TRUE(mCallbackInvoked);
// When callback returns false, parsing should stop early
// So we shouldn't get all the hosts
}
TEST_F(TestEtcHostsParsing, EmptyFile) {
sCurrentTest = this ;
// Create empty file
nsCOMPtr<nsIOutputStream> stream;
nsresult rv =
NS_NewLocalFileOutputStream(getter_AddRefs(stream), mTestHostsFile);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rv = stream->Close();
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsAutoCString path;
rv = GetCrossplatformNativePath(mTestHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rust_parse_etc_hosts(&path, TestCallback);
// Callback might or might not be invoked for empty files, but shouldn't crash
EXPECT_EQ(mParsedHosts.Length(), 0 u);
}
TEST_F(TestEtcHostsParsing, FileWithOnlyComments) {
sCurrentTest = this ;
nsCOMPtr<nsIOutputStream> stream;
nsresult rv =
NS_NewLocalFileOutputStream(getter_AddRefs(stream), mTestHostsFile);
ASSERT_TRUE(NS_SUCCEEDED(rv));
const char * content =
"# This is a comment\n"
"# Another comment\n"
" # Indented comment\n" ;
uint32_t written;
rv = stream->Write(content, strlen(content), &written);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rv = stream->Close();
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsAutoCString path;
rv = GetCrossplatformNativePath(mTestHostsFile, path);
ASSERT_TRUE(NS_SUCCEEDED(rv));
rust_parse_etc_hosts(&path, TestCallback);
// Should not parse any hosts from a file with only comments
EXPECT_EQ(mParsedHosts.Length(), 0 u);
}
Messung V0.5 in Prozent C=92 H=98 G=94
¤ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691