blob: bef4765e1f1f0efe2ee6fba10ae5511d3efdc958 [file] [log] [blame]
// Unit test for sandboxed_hunspell
#include <optional>
#include <string>
#include "devtools/build/runtime/get_runfiles_dir.h"
#include "file/base/filelineiter.h"
#include "file/base/path.h"
#include "testing/base/public/benchmark.h"
#include "testing/base/public/gmock.h"
#include "testing/base/public/gunit.h"
#include "third_party/absl/log/check.h"
#include "third_party/absl/log/log.h"
#include "third_party/absl/status/status.h"
#include "third_party/absl/strings/string_view.h"
#include "third_party/hunspell/sandbox/sandbox.h"
#include "third_party/hunspell/sandbox/sandboxed_hunspell.sapi.h"
#include "third_party/hunspell/src/hunspell/hunspell.h"
#include "third_party/sandboxed_api/util/status_matchers.h"
#include "third_party/sandboxed_api/vars.h"
namespace {
static constexpr absl::string_view test_dir_ =
"/tests";
static constexpr absl::string_view kAffixFileName = "utf8.aff";
static constexpr absl::string_view kDictionaryFileName = "utf8.dic";
static constexpr absl::string_view kGoodFileName = "utf8.good";
static constexpr absl::string_view kWrongFileName = "utf8_nonbmp.wrong";
static constexpr absl::string_view kSuggestion = "fo";
static constexpr absl::string_view kRandomWord = "random_word123";
std::string GetTestFilePath(const absl::string_view& filename) {
return file::JoinPath(devtools_build::GetDataDependencyFilepath(test_dir_),
filename);
}
class HunspellTest : public ::testing::Test {
public:
HunspellTest()
: s_affix_filename_(GetTestFilePath(kAffixFileName)),
s_dictionary_filename_(GetTestFilePath(kDictionaryFileName)),
sandbox_(s_affix_filename_, s_dictionary_filename_),
api_(&sandbox_) {
sapi::v::ConstCStr c_affix_filename(s_affix_filename_.c_str());
sapi::v::ConstCStr c_dictionary_filename(s_dictionary_filename_.c_str());
CHECK(sandbox_.Init().ok());
absl::StatusOr<sandboxed_hunspell::Hunhandle*> hunspell =
api_.Hunspell_create(c_affix_filename.PtrBefore(),
c_dictionary_filename.PtrBefore());
CHECK(hunspell.ok());
hunspellrp_.emplace(*hunspell);
}
~HunspellTest() override {
if (hunspellrp_) {
absl::Status status = api_.Hunspell_destroy(&(*hunspellrp_));
CHECK(status.ok());
}
}
protected:
const std::string s_affix_filename_;
const std::string s_dictionary_filename_;
sandboxed_hunspell::LibHunspellSapiSandbox sandbox_;
sandboxed_hunspell::LibHunspellApi api_;
std::optional<sapi::v::RemotePtr> hunspellrp_;
};
TEST_F(HunspellTest, CheckEncoding) {
SAPI_ASSERT_OK_AND_ASSIGN(char* ret,
api_.Hunspell_get_dic_encoding(&(*hunspellrp_)));
SAPI_ASSERT_OK_AND_ASSIGN(std::string encoding, api_.GetSandbox()->GetCString(
sapi::v::RemotePtr(ret)));
EXPECT_EQ(encoding, "UTF-8");
}
TEST_F(HunspellTest, CheckGoodSpell) {
SAPI_ASSERT_OK_AND_ASSIGN(char* _,
api_.Hunspell_get_dic_encoding(&(*hunspellrp_)));
for (std::string& buf :
FileLines(GetTestFilePath(kGoodFileName), FileLineIterator::NO_LF)) {
sapi::v::ConstCStr cbuf(buf.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
int result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 1);
}
}
TEST_F(HunspellTest, CheckWrongSpell) {
SAPI_ASSERT_OK_AND_ASSIGN(char* _,
api_.Hunspell_get_dic_encoding(&(*hunspellrp_)));
for (std::string& buf :
FileLines(GetTestFilePath(kWrongFileName), FileLineIterator::NO_LF)) {
sapi::v::ConstCStr cbuf(buf.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
int result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
}
}
TEST_F(HunspellTest, CheckAddToDict) {
sapi::v::ConstCStr cbuf(kRandomWord.data());
int result;
SAPI_ASSERT_OK_AND_ASSIGN(
result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
SAPI_ASSERT_OK_AND_ASSIGN(
result, api_.Hunspell_add(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
SAPI_ASSERT_OK_AND_ASSIGN(
result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 1);
SAPI_ASSERT_OK_AND_ASSIGN(
result, api_.Hunspell_remove(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
SAPI_ASSERT_OK_AND_ASSIGN(
result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
}
TEST_F(HunspellTest, CheckSuggestion) {
sapi::v::ConstCStr cbuf(kSuggestion.data());
SAPI_ASSERT_OK_AND_ASSIGN(
int result, api_.Hunspell_spell(&(*hunspellrp_), cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
sapi::v::GenericPtr outptr;
SAPI_ASSERT_OK_AND_ASSIGN(
int nlist, api_.Hunspell_suggest(&(*hunspellrp_), outptr.PtrAfter(),
cbuf.PtrBefore()));
ASSERT_GT(nlist, 0);
}
// The following go/benchmark tests allow viewing the performance overhead
// the sandboxed API causes. Due to the nature of SAPI sandboxes, the only
// reliable metric is `time/op`.
// Please note that every SAPI sandbox cold start takes ~10ms.
//
// benchy --perflab --runs 20 -benchtime 0.5s \
// //third_party/hunspell/sandbox:sandboxed_hunspell_test
//
// name time/op
// BM_Sandboxed_hunspell_spell 228µs ± 2%
// BM_Unsandboxed_hunspell_spell 133µs ± 5%
void BM_Sandboxed_hunspell_spell(benchmark::State& state) {
std::string suggestion = "fo";
std::string s_affix_filename = GetTestFilePath("utf8.aff");
std::string s_dictionary_filename = GetTestFilePath("utf8.dic");
sandboxed_hunspell::LibHunspellSapiSandbox sandbox(s_affix_filename,
s_dictionary_filename);
ASSERT_OK(sandbox.Init());
sandboxed_hunspell::LibHunspellApi api(&sandbox);
sapi::v::ConstCStr c_affix_filename(s_affix_filename.c_str());
sapi::v::ConstCStr c_dictionary_filename(s_dictionary_filename.c_str());
sapi::v::ConstCStr cbuf(suggestion.c_str());
for (const auto _ : state) {
SAPI_ASSERT_OK_AND_ASSIGN(
sandboxed_hunspell::Hunhandle * hunspell,
api.Hunspell_create(c_affix_filename.PtrBefore(),
c_dictionary_filename.PtrBefore()));
sapi::v::RemotePtr hunspellrp(hunspell);
SAPI_ASSERT_OK_AND_ASSIGN(
int result, api.Hunspell_spell(&hunspellrp, cbuf.PtrBefore()));
ASSERT_EQ(result, 0);
absl::Status status = api.Hunspell_destroy(&hunspellrp);
ASSERT_THAT(status, sapi::IsOk());
}
}
void BM_Unsandboxed_hunspell_spell(benchmark::State& state) {
std::string suggestion = "fo";
for (const auto _ : state) {
Hunhandle* hunspell = Hunspell_create(GetTestFilePath("utf8.aff").c_str(),
GetTestFilePath("utf8.dic").c_str());
ASSERT_EQ(Hunspell_spell(hunspell, suggestion.c_str()), 0);
Hunspell_destroy(hunspell);
}
}
BENCHMARK(BM_Sandboxed_hunspell_spell);
BENCHMARK(BM_Unsandboxed_hunspell_spell);
} // namespace