blob: 330e0059e17566100697a32169ccf4b1d1564dac [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Test.h"
#include "Utilities.h"
#include "WTFStringUtilities.h"
#include <WebCore/FileMonitor.h>
#include <wtf/FileSystem.h>
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
#include <wtf/Scope.h>
#include <wtf/StringExtras.h>
#include <wtf/WorkQueue.h>
#include <wtf/text/StringBuffer.h>
// Note: Disabling iOS since 'system' is not available on that platform.
#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(WPE)
using namespace WebCore;
namespace TestWebKitAPI {
const String FileMonitorTestData("This is a test"_s);
const String FileMonitorRevisedData("This is some changed text for the test"_s);
const String FileMonitorSecondRevisedData("This is some changed text for the test"_s);
class FileMonitorTest : public testing::Test {
public:
void SetUp() override
{
WTF::initializeMainThread();
// create temp file
FileSystem::PlatformFileHandle handle;
m_tempFilePath = FileSystem::openTemporaryFile("tempTestFile"_s, handle);
ASSERT_NE(handle, FileSystem::invalidPlatformFileHandle);
int rc = FileSystem::writeToFile(handle, FileMonitorTestData.utf8().data(), FileMonitorTestData.length());
ASSERT_NE(rc, -1);
FileSystem::closeFile(handle);
}
void TearDown() override
{
FileSystem::deleteFile(m_tempFilePath);
}
const String& tempFilePath() { return m_tempFilePath; }
private:
String m_tempFilePath;
};
static bool observedFileModification = false;
static bool observedFileDeletion = false;
static bool didFinish = false;
static void handleFileModification()
{
observedFileModification = true;
didFinish = true;
}
static void handleFileDeletion()
{
observedFileDeletion = true;
didFinish = true;
}
static void resetTestState()
{
observedFileModification = false;
observedFileDeletion = false;
didFinish = false;
}
static String createCommand(const String& path, const String& payload)
{
return makeString("echo \"", payload, "\" > ", path);
}
static String readContentsOfFile(const String& path)
{
auto buffer = FileSystem::readEntireFile(path);
if (!buffer)
return emptyString();
String result(static_cast<const LChar*>(buffer->data()), buffer->size());
if (result.endsWith('\n'))
return result.left(result.length() - 1);
return result;
}
TEST_F(FileMonitorTest, DetectChange)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
WTF::initializeMainThread();
auto testQueue = WorkQueue::create("Test Work Queue");
auto monitor = makeUnique<FileMonitor>(tempFilePath(), testQueue.copyRef(), [] (FileMonitor::FileChangeType type) {
ASSERT(!RunLoop::isMain());
switch (type) {
case FileMonitor::FileChangeType::Modification:
handleFileModification();
break;
case FileMonitor::FileChangeType::Removal:
handleFileDeletion();
break;
}
});
testQueue->dispatch([this] () mutable {
String fileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorTestData.utf8().data(), fileContents.utf8().data());
auto command = createCommand(tempFilePath(), FileMonitorRevisedData);
auto rc = system(command.utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_TRUE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
String revisedFileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorRevisedData.utf8().data(), revisedFileContents.utf8().data());
resetTestState();
}
TEST_F(FileMonitorTest, DetectMultipleChanges)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
WTF::initializeMainThread();
auto testQueue = WorkQueue::create("Test Work Queue");
auto monitor = makeUnique<FileMonitor>(tempFilePath(), testQueue.copyRef(), [] (FileMonitor::FileChangeType type) {
ASSERT(!RunLoop::isMain());
switch (type) {
case FileMonitor::FileChangeType::Modification:
handleFileModification();
break;
case FileMonitor::FileChangeType::Removal:
handleFileDeletion();
break;
}
});
testQueue->dispatch([this] () mutable {
String fileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorTestData.utf8().data(), fileContents.utf8().data());
auto firstCommand = createCommand(tempFilePath(), FileMonitorRevisedData);
auto rc = system(firstCommand.utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_TRUE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
String revisedFileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorRevisedData.utf8().data(), revisedFileContents.utf8().data());
resetTestState();
testQueue->dispatch([this] () mutable {
auto secondCommand = createCommand(tempFilePath(), FileMonitorSecondRevisedData);
auto rc = system(secondCommand.utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_TRUE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
String secondRevisedfileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorSecondRevisedData.utf8().data(), secondRevisedfileContents.utf8().data());
resetTestState();
}
TEST_F(FileMonitorTest, DetectDeletion)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
WTF::initializeMainThread();
auto testQueue = WorkQueue::create("Test Work Queue");
auto monitor = makeUnique<FileMonitor>(tempFilePath(), testQueue.copyRef(), [] (FileMonitor::FileChangeType type) {
ASSERT(!RunLoop::isMain());
switch (type) {
case FileMonitor::FileChangeType::Modification:
handleFileModification();
break;
case FileMonitor::FileChangeType::Removal:
handleFileDeletion();
break;
}
});
testQueue->dispatch([this] () mutable {
auto rc = system(makeString("rm -f ", tempFilePath()).utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_FALSE(observedFileModification);
EXPECT_TRUE(observedFileDeletion);
resetTestState();
}
TEST_F(FileMonitorTest, DetectChangeAndThenDelete)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
WTF::initializeMainThread();
auto testQueue = WorkQueue::create("Test Work Queue");
auto monitor = makeUnique<FileMonitor>(tempFilePath(), testQueue.copyRef(), [] (FileMonitor::FileChangeType type) {
ASSERT(!RunLoop::isMain());
switch (type) {
case FileMonitor::FileChangeType::Modification:
handleFileModification();
break;
case FileMonitor::FileChangeType::Removal:
handleFileDeletion();
break;
}
});
testQueue->dispatch([this] () mutable {
String fileContents = readContentsOfFile(tempFilePath());
EXPECT_STREQ(FileMonitorTestData.utf8().data(), fileContents.utf8().data());
auto firstCommand = createCommand(tempFilePath(), FileMonitorRevisedData);
auto rc = system(firstCommand.utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_TRUE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
resetTestState();
testQueue->dispatch([this] () mutable {
auto rc = system(makeString("rm -f ", tempFilePath()).utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_FALSE(observedFileModification);
EXPECT_TRUE(observedFileDeletion);
resetTestState();
}
TEST_F(FileMonitorTest, DetectDeleteButNotSubsequentChange)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
WTF::initializeMainThread();
auto testQueue = WorkQueue::create("Test Work Queue");
auto monitor = makeUnique<FileMonitor>(tempFilePath(), testQueue.copyRef(), [] (FileMonitor::FileChangeType type) {
ASSERT(!RunLoop::isMain());
switch (type) {
case FileMonitor::FileChangeType::Modification:
handleFileModification();
break;
case FileMonitor::FileChangeType::Removal:
handleFileDeletion();
break;
}
});
testQueue->dispatch([this] () mutable {
auto rc = system(makeString("rm -f ", tempFilePath()).utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
Util::run(&didFinish);
EXPECT_FALSE(observedFileModification);
EXPECT_TRUE(observedFileDeletion);
resetTestState();
testQueue->dispatch([this] () mutable {
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::Write);
ASSERT_NE(handle, FileSystem::invalidPlatformFileHandle);
int rc = FileSystem::writeToFile(handle, FileMonitorTestData.utf8().data(), FileMonitorTestData.length());
ASSERT_NE(rc, -1);
auto firstCommand = createCommand(tempFilePath(), FileMonitorRevisedData);
rc = system(firstCommand.utf8().data());
ASSERT_NE(rc, -1);
if (rc == -1)
didFinish = true;
});
// Set a timer to end the test, since we do not expect the file modification
// to be observed.
testQueue->dispatchAfter(500_ms, [&](void) {
EXPECT_FALSE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
didFinish = true;
});
Util::run(&didFinish);
EXPECT_FALSE(observedFileModification);
EXPECT_FALSE(observedFileDeletion);
resetTestState();
}
}
#endif