blob: fdc92092fb189f9de306e59560c4051a9b54d3d3 [file] [log] [blame]
// fst_test.h
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright 2005-2010 Google, Inc.
// Author: riley@google.com (Michael Riley)
//
// \file
// Regression test for FST classes.
#ifndef FST_TEST_FST_TEST_H_
#define FST_TEST_FST_TEST_H_
#include <fst/equal.h>
#include <fst/matcher.h>
#include <fst/vector-fst.h>
#include <fst/verify.h>
DECLARE_string(tmpdir);
namespace fst {
// This tests an Fst F that is assumed to have a copy method from an
// arbitrary Fst. Some test functions make further assumptions mostly
// obvious from their name. These tests are written as member temple
// functions that take a test fst as its argument so that different
// Fsts in the interface hierarchy can be tested separately and so
// that we can instantiate only those tests that make sense for a
// particular Fst.
template <class F>
class FstTester {
public:
typedef typename F::Arc Arc;
typedef typename Arc::StateId StateId;
typedef typename Arc::Weight Weight;
typedef typename Arc::Label Label;
FstTester() {
VectorFst<Arc> vfst;
InitFst(&vfst, 128);
testfst_ = new F(vfst);
}
explicit FstTester(F *testfst) : testfst_(testfst) { }
~FstTester() {
delete testfst_;
}
// This verifies the contents described in InitFst() using
// methods defined in a generic Fst.
template <class G>
void TestBase(const G &fst) const {
CHECK(Verify(fst));
CHECK_EQ(fst.Start(), 0);
StateId ns = 0;
StateIterator<G> siter(fst);
Matcher<G> matcher(fst, MATCH_INPUT);
MatchType match_type = matcher.Type(true);
for (; !siter.Done(); siter.Next()) {}
for (siter.Reset(); !siter.Done(); siter.Next()) {
StateId s = siter.Value();
matcher.SetState(s);
CHECK_EQ(fst.Final(s), NthWeight(s));
size_t na = 0;
ArcIterator<G> aiter(fst, s);
for (; !aiter.Done(); aiter.Next()) {}
for (aiter.Reset(); !aiter.Done(); aiter.Next()) {
++na;
const Arc &arc = aiter.Value();
CHECK_EQ(arc.ilabel, na);
CHECK_EQ(arc.olabel, 0);
CHECK_EQ(arc.weight, NthWeight(na));
CHECK_EQ(arc.nextstate, s);
if (match_type == MATCH_INPUT) {
CHECK(matcher.Find(arc.ilabel));
CHECK_EQ(matcher.Value().ilabel, arc.ilabel);
}
}
CHECK_EQ(na, s);
CHECK_EQ(na, aiter.Position());
CHECK_EQ(fst.NumArcs(s), s);
CHECK_EQ(fst.NumInputEpsilons(s), 0);
CHECK_EQ(fst.NumOutputEpsilons(s), s);
CHECK(!matcher.Find(s + 1)); // out-of-range
CHECK(!matcher.Find(kNoLabel)); // no explicit epsilons
CHECK(matcher.Find(0));
CHECK_EQ(matcher.Value().ilabel, kNoLabel); // implicit epsilon loop
++ns;
}
CHECK(fst.Properties(kNotAcceptor, true));
CHECK(fst.Properties(kOEpsilons, true));
}
void TestBase() const {
TestBase(*testfst_);
}
// This verifies methods specfic to an ExpandedFst.
template <class G>
void TestExpanded(const G &fst) const {
StateId ns = 0;
for (StateIterator<G> siter(fst);
!siter.Done();
siter.Next()) {
++ns;
}
CHECK_EQ(fst.NumStates(), ns);
CHECK(fst.Properties(kExpanded, false));
}
void TestExpanded() const { TestExpanded(*testfst_); }
// This verifies methods specific to a MutableFst.
template <class G>
void TestMutable(G *fst) const {
for (StateIterator<G> siter(*fst);
!siter.Done();
siter.Next()) {
StateId s = siter.Value();
size_t na = 0;
size_t ni = fst->NumInputEpsilons(s);
MutableArcIterator<G> aiter(fst, s);
for (; !aiter.Done(); aiter.Next()) {}
for (aiter.Reset(); !aiter.Done(); aiter.Next()) {
++na;
Arc arc = aiter.Value();
arc.ilabel = 0;
aiter.SetValue(arc);
arc = aiter.Value();
CHECK_EQ(arc.ilabel, 0);
CHECK_EQ(fst->NumInputEpsilons(s), ni + 1);
arc.ilabel = na;
aiter.SetValue(arc);
CHECK_EQ(fst->NumInputEpsilons(s), ni);
}
}
G *cfst1 = fst->Copy();
cfst1->DeleteStates();
CHECK_EQ(cfst1->NumStates(), 0);
delete cfst1;
G *cfst2 = fst->Copy();
for (StateIterator<G> siter(*cfst2);
!siter.Done();
siter.Next()) {
StateId s = siter.Value();
cfst2->DeleteArcs(s);
CHECK_EQ(cfst2->NumArcs(s), 0);
CHECK_EQ(cfst2->NumInputEpsilons(s), 0);
CHECK_EQ(cfst2->NumOutputEpsilons(s), 0);
}
delete cfst2;
}
void TestMutable() { TestMutable(testfst_); }
// This verifies the copy methods.
template <class G>
void TestAssign(G *fst) const {
// Assignment from G
G afst1;
afst1 = *fst;
CHECK(Equal(*fst, afst1));
// Assignment from Fst
G afst2;
afst2 = *static_cast<const Fst<Arc> *>(fst);
CHECK(Equal(*fst, afst2));
// Assignment from self
afst2.operator=(afst2);
CHECK(Equal(*fst, afst2));
}
void TestAssign() { TestAssign(testfst_); }
// This verifies the copy methods.
template <class G>
void TestCopy(const G &fst) const {
// Copy from G
G c1fst(fst);
TestBase(c1fst);
// Copy from Fst
const G c2fst(static_cast<const Fst<Arc> &>(fst));
TestBase(c2fst);
// Copy from self
const G *c3fst = fst.Copy();
TestBase(*c3fst);
delete c3fst;
}
void TestCopy() const { TestCopy(*testfst_); }
// This verifies the read/write methods.
template <class G>
void TestIO(const G &fst) const {
const string filename = FLAGS_tmpdir + "/test.fst";
const string aligned = FLAGS_tmpdir + "/aligned.fst";
{
// write/read
CHECK(fst.Write(filename));
G *ffst = G::Read(filename);
CHECK(ffst);
TestBase(*ffst);
delete ffst;
}
{
// generic read/cast/test
Fst<Arc> *gfst = Fst<Arc>::Read(filename);
CHECK(gfst);
G *dfst = static_cast<G *>(gfst);
TestBase(*dfst);
// generic write/read/test
CHECK(gfst->Write(filename));
Fst<Arc> *hfst = Fst<Arc>::Read(filename);
CHECK(hfst);
TestBase(*hfst);
delete gfst;
delete hfst;
}
{
// check mmaping by first writing the file with the aligned attribute set
{
ofstream ostr(aligned.c_str());
FstWriteOptions opts;
opts.source = aligned;
opts.align = true;
CHECK(fst.Write(ostr, opts));
}
ifstream istr(aligned.c_str());
FstReadOptions opts;
opts.mode = FstReadOptions::ReadMode("map");
opts.source = aligned;
G *gfst = G::Read(istr, opts);
CHECK(gfst);
TestBase(*gfst);
delete gfst;
}
// check mmaping of unaligned files to make sure it does not fail.
{
{
ofstream ostr(aligned.c_str());
FstWriteOptions opts;
opts.source = aligned;
opts.align = false;
CHECK(fst.Write(ostr, opts));
}
ifstream istr(aligned.c_str());
FstReadOptions opts;
opts.mode = FstReadOptions::ReadMode("map");
opts.source = aligned;
G *gfst = G::Read(istr, opts);
CHECK(gfst);
TestBase(*gfst);
delete gfst;
}
// expanded write/read/test
if (fst.Properties(kExpanded, false)) {
ExpandedFst<Arc> *efst = ExpandedFst<Arc>::Read(filename);
CHECK(efst);
TestBase(*efst);
TestExpanded(*efst);
delete efst;
}
// mutable write/read/test
if (fst.Properties(kMutable, false)) {
MutableFst<Arc> *mfst = MutableFst<Arc>::Read(filename);
CHECK(mfst);
TestBase(*mfst);
TestExpanded(*mfst);
TestMutable(mfst);
delete mfst;
}
}
void TestIO() const { TestIO(*testfst_); }
private:
// This constructs test FSTs. Given a mutable FST, will leave
// the FST as follows:
// (I) NumStates() = nstates
// (II) Start() = 0
// (III) Final(s) = NthWeight(s)
// (IV) For state s:
// (a) NumArcs(s) == s
// (b) For ith arc of s:
// (1) ilabel = i
// (2) olabel = 0
// (3) weight = NthWeight(i)
// (4) nextstate = s
void InitFst(MutableFst<Arc> *fst, size_t nstates) const {
fst->DeleteStates();
CHECK_GT(nstates, 0);
for (StateId s = 0; s < nstates; ++s) {
fst->AddState();
fst->SetFinal(s, NthWeight(s));
for (size_t i = 1; i <= s; ++i) {
Arc arc(i, 0, NthWeight(i), s);
fst->AddArc(s, arc);
}
}
fst->SetStart(0);
}
// Generates One() + ... + One() (n times)
Weight NthWeight(int n) const {
Weight w = Weight::Zero();
for (int i = 0; i < n; ++i)
w = Plus(w, Weight::One());
return w;
}
F *testfst_; // what we're testing
};
} // namespace fst
#endif // FST_TEST_FST_TEST_H_