Exheredludis/paludis/util/config_file_TEST.cc
Saleem Abdulrasool d81457e420 modernize: use nullptr rather than 0 or NULL
Automated conversion to nullptr.  NFC.
2016-08-04 22:11:01 -07:00

510 lines
18 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 Ciaran McCreesh
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <paludis/util/config_file.hh>
#include <paludis/util/fs_path.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/system.hh>
#include <paludis/util/map.hh>
#include <paludis/util/options.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <sstream>
#include <vector>
#include <cstdlib>
#include <iomanip>
#include <unistd.h>
#include <gtest/gtest.h>
using namespace paludis;
namespace
{
std::string predefined(const std::shared_ptr<const Map<std::string, std::string> > & m,
const KeyValueConfigFile &, const std::string & s)
{
if (m->end() == m->find(s))
return "";
return m->find(s)->second;
}
std::string predefined_from_env(const KeyValueConfigFile &, const std::string & s)
{
return getenv_with_default(s, "");
}
std::string predefined_from_config_file(const KeyValueConfigFile & f,
const KeyValueConfigFile &, const std::string & s)
{
return f.get(s);
}
std::string concatenate(const KeyValueConfigFile &, const std::string &, const std::string & prev, const std::string & value)
{
return prev + value;
}
}
TEST(LineConfigFile, Works)
{
std::stringstream s;
s << "one" << std::endl;
s << " two \t " << std::endl;
s << " \t " << std::endl;
s << "" << std::endl;
s << "three" << std::endl;
s << "# blah" << std::endl;
s << " # blah" << std::endl;
s << "#" << std::endl;
s << " # \t " << std::endl;
s << "four four" << std::endl;
s << "five \\" << std::endl;
s << "six" << std::endl;
s << "seven\\" << std::endl;
s << "eight" << std::endl;
s << "nine # ten" << std::endl;
LineConfigFile f(s, { });
ASSERT_EQ(7, std::distance(f.begin(), f.end()));
std::vector<std::string> lines;
std::copy(f.begin(), f.end(), std::back_inserter(lines));
EXPECT_EQ("one", lines.at(0));
EXPECT_EQ("two", lines.at(1));
EXPECT_EQ("three", lines.at(2));
EXPECT_EQ("four four", lines.at(3));
EXPECT_EQ("five six", lines.at(4));
EXPECT_EQ("seveneight", lines.at(5));
EXPECT_EQ("nine # ten", lines.at(6));
s.clear();
s.seekg(0, std::ios::beg);
LineConfigFile g(s, { lcfo_disallow_comments, lcfo_preserve_whitespace, lcfo_no_skip_blank_lines });
ASSERT_EQ(13, std::distance(g.begin(), g.end()));
lines.clear();
std::copy(g.begin(), g.end(), std::back_inserter(lines));
EXPECT_EQ("one", lines.at(0));
EXPECT_EQ(" two \t ", lines.at(1));
EXPECT_EQ(" \t ", lines.at(2));
EXPECT_EQ("", lines.at(3));
EXPECT_EQ("three", lines.at(4));
EXPECT_EQ("# blah", lines.at(5));
EXPECT_EQ(" # blah", lines.at(6));
EXPECT_EQ("#", lines.at(7));
EXPECT_EQ(" # \t ", lines.at(8));
EXPECT_EQ("four four", lines.at(9));
EXPECT_EQ("five six", lines.at(10));
EXPECT_EQ("seveneight", lines.at(11));
EXPECT_EQ("nine # ten", lines.at(12));
s.clear();
s.seekg(0, std::ios::beg);
LineConfigFile h(s, { lcfo_allow_inline_comments });
ASSERT_EQ(7, std::distance(f.begin(), f.end()));
lines.clear();
std::copy(h.begin(), h.end(), std::back_inserter(lines));
EXPECT_EQ("one", lines.at(0));
EXPECT_EQ("two", lines.at(1));
EXPECT_EQ("three", lines.at(2));
EXPECT_EQ("four four", lines.at(3));
EXPECT_EQ("five six", lines.at(4));
EXPECT_EQ("seveneight", lines.at(5));
EXPECT_EQ("nine", lines.at(6));
}
TEST(LineConfigFile, Open)
{
FSPath ff("config_file_TEST_dir/config_file");
EXPECT_TRUE(ff.stat().is_regular_file());
LineConfigFile f(ff, { });
ASSERT_EQ(1, std::distance(f.begin(), f.end()));
EXPECT_EQ("I am a fish.", *f.begin());
FSPath ff2("config_file_TEST_dir/not_a_config_file");
EXPECT_TRUE(! ff2.stat().exists());
LineConfigFile * PALUDIS_ATTRIBUTE((unused)) f2(nullptr);
EXPECT_THROW(f2 = new LineConfigFile(ff2, { }), ConfigFileError);
if (0 != geteuid())
{
FSPath ff3("config_file_TEST_dir/unreadable_file");
EXPECT_TRUE(ff3.stat().is_regular_file());
LineConfigFile * PALUDIS_ATTRIBUTE((unused)) f3(nullptr);
EXPECT_THROW(f3 = new LineConfigFile(ff3, { }), ConfigFileError);
}
}
TEST(KeyValueConfigFile, Works)
{
std::stringstream s;
s << "one=first" << std::endl;
s << "two = second" << std::endl;
s << "three=" << std::endl;
s << "four = \"fourth\" " << std::endl;
s << "five = ''" << std::endl;
KeyValueConfigFile ff(s, { kvcfo_preserve_whitespace },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("first", ff.get("one"));
EXPECT_EQ("second", ff.get("two"));
EXPECT_EQ("", ff.get("three"));
EXPECT_EQ("fourth ", ff.get("four"));
EXPECT_EQ("", ff.get("five"));
EXPECT_EQ("", ff.get("six"));
}
TEST(KeyValueConfigFile, Continuations)
{
std::stringstream s;
s << "one='first" << std::endl;
s << " first " << std::endl;
s << "first'" << std::endl;
KeyValueConfigFile ff(s, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("first\n first \nfirst", ff.get("one"));
}
TEST(KeyValueConfigFile, Variables)
{
std::stringstream s;
s << "x=foo" << std::endl;
s << "y = \"${x}\\\\${y}\\$${z}\"" << std::endl;
s << "z = $x$y$z" << std::endl;
KeyValueConfigFile ff(s, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("foo", ff.get("x"));
EXPECT_EQ("foo\\$", ff.get("y"));
EXPECT_EQ("foofoo\\$", ff.get("z"));
std::stringstream t;
std::shared_ptr<Map<std::string, std::string> > t_defs(std::make_shared<Map<std::string, std::string>>());
t_defs->insert("a", "moo");
t_defs->insert("d", "bar");
t_defs->insert("e", "baz");
t << "a=foo" << std::endl;
t << "b=$a" << std::endl;
t << "c=$d" << std::endl;
t << "d=$d" << std::endl;
t << "f = " << std::endl;
t << "g = foo \\" << std::endl;
t << " bar" << std::endl;
KeyValueConfigFile fg(t, { },
std::bind(&predefined, t_defs, std::placeholders::_1, std::placeholders::_2),
&KeyValueConfigFile::no_transformation);
EXPECT_EQ("foo", fg.get("a"));
EXPECT_EQ("foo", fg.get("b"));
EXPECT_EQ("bar", fg.get("c"));
EXPECT_EQ("bar", fg.get("d"));
EXPECT_EQ("baz", fg.get("e"));
EXPECT_EQ("", fg.get("f"));
EXPECT_EQ("foo bar", fg.get("g"));
}
TEST(KeyValueConfigFile, Defaults)
{
setenv("moo", "cow", 1);
std::stringstream d_s;
d_s << "foo=oink" << std::endl;
std::shared_ptr<KeyValueConfigFile> d_ff(std::make_shared<KeyValueConfigFile>(d_s, KeyValueConfigFileOptions(),
&predefined_from_env,
&KeyValueConfigFile::no_transformation));
std::stringstream s;
s << "x=${foo}" << std::endl;
s << "y=${moo}" << std::endl;
KeyValueConfigFile ff(s, { },
std::bind(&predefined_from_config_file, std::cref(*d_ff), std::placeholders::_1, std::placeholders::_2),
&KeyValueConfigFile::no_transformation);
EXPECT_EQ("oink", ff.get("x"));
EXPECT_EQ("cow", ff.get("y"));
}
TEST(KeyValueConfigFile, DefaultsAndTransformations)
{
auto predef(std::make_shared<Map<std::string, std::string>>());
predef->insert("magic", "xyzzy");
std::stringstream d_s;
d_s << "magic=plugh" << std::endl;
KeyValueConfigFile ff(d_s, { },
std::bind(&predefined, predef, std::placeholders::_1, std::placeholders::_2),
&concatenate);
EXPECT_EQ("xyzzyplugh", ff.get("magic"));
}
TEST(KeyValueConfigFile, Source)
{
std::stringstream d_s;
d_s << "six=\"llama\"" << std::endl;
d_s << "seven=\"spider\"" << std::endl;
d_s << "source config_file_TEST_dir/sourced_one" << std::endl;
d_s << "eight=\"octopus\"" << std::endl;
KeyValueConfigFile ff(d_s, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("cat", ff.get("one"));
EXPECT_EQ("dog", ff.get("two"));
EXPECT_EQ("koala", ff.get("three"));
EXPECT_EQ("sheep", ff.get("four"));
EXPECT_EQ("rabbit", ff.get("five"));
EXPECT_EQ("llama", ff.get("six"));
EXPECT_EQ("spider", ff.get("seven"));
EXPECT_EQ("octopus", ff.get("eight"));
}
TEST(KeyValueConfigFile, IgnoreExport)
{
std::stringstream d_s;
d_s << "export \t foo = \"xyzzy\"" << std::endl;
d_s << "export bar='plugh'" << std::endl;
d_s << "baz = \"plover\"" << std::endl;
d_s << "exportfoo = \"exportxyzzy\"" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_ignore_export },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("xyzzy", ff.get("foo"));
EXPECT_EQ("plugh", ff.get("bar"));
EXPECT_EQ("plover", ff.get("baz"));
EXPECT_EQ("exportxyzzy", ff.get("exportfoo"));
std::stringstream d_s2;
d_s2 << "export = 42" << std::endl;
KeyValueConfigFile ff2(d_s2, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
EXPECT_EQ("42", ff2.get("export"));
}
TEST(KeyValueConfigFile, Errors)
{
std::stringstream s1;
s1 << "x='" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s1, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s2;
s2 << "x='moo\"" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s2, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s3;
s3 << "x=${foo" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s3, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s4;
s4 << "x=$~" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s4, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s5;
s5 << "x=abc\\" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s5, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s6;
s6 << "x=$" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s6, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s7;
s7 << "x=blah \\" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s7, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s8;
s8 << "x=blah \\" << std::endl << "# foo" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s8, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s9;
s9 << "x='blah" << std::endl << "blah" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s9, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s10;
s10 << "export x=blah" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s10, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s11;
s11 << "export x = blah" << std::endl;
EXPECT_THROW(KeyValueConfigFile ff(s11, { kvcfo_ignore_export, kvcfo_disallow_space_around_equals },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
}
TEST(LineConfigFile, InlineComments)
{
std::stringstream s;
s << "one # moo" << std::endl;
s << "tw\\" << std::endl;
s << "o # moo" << std::endl;
s << "" << std::endl;
s << "three #" << std::endl;
s << "#" << std::endl;
s << "four" << std::endl;
s << "five # moo" << std::endl;
LineConfigFile f(s, { lcfo_allow_inline_comments });
ASSERT_EQ(5, std::distance(f.begin(), f.end()));
std::vector<std::string> lines;
std::copy(f.begin(), f.end(), std::back_inserter(lines));
EXPECT_EQ("one", lines.at(0));
EXPECT_EQ("two", lines.at(1));
EXPECT_EQ("three", lines.at(2));
EXPECT_EQ("four", lines.at(3));
EXPECT_EQ("five", lines.at(4));
}
TEST(KeyValueConfigFile, InlineComments)
{
std::stringstream d_s;
d_s << "one=\"one\" # foo" << std::endl;
d_s << "two=two # bar" << std::endl;
d_s << "three = \\" << std::endl;
d_s << "three # bar" << std::endl;
d_s << "four = four # foo" << std::endl;
d_s << "five = five # moo" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_allow_inline_comments },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(5, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("one", ff.get("one"));
EXPECT_EQ("two", ff.get("two"));
EXPECT_EQ("three", ff.get("three"));
EXPECT_EQ("four", ff.get("four"));
EXPECT_EQ("five", ff.get("five"));
}
TEST(KeyValueConfigFile, MultipleAssigns)
{
std::stringstream d_s;
d_s << "one=\"one\" two=two" << std::endl;
d_s << "three = \\" << std::endl;
d_s << "three four = \\" << std::endl;
d_s << "\"four\" # one=three" << std::endl;
d_s << "five=five # six=six" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_allow_inline_comments, kvcfo_allow_multiple_assigns_per_line, kvcfo_disallow_space_inside_unquoted_values },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(5, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("one", ff.get("one"));
EXPECT_EQ("two", ff.get("two"));
EXPECT_EQ("three", ff.get("three"));
EXPECT_EQ("four", ff.get("four"));
EXPECT_EQ("five", ff.get("five"));
}
TEST(KeyValueConfigFile, Sections)
{
std::stringstream d_s;
d_s << "a = b" << std::endl;
d_s << "c = d" << std::endl;
d_s << "[foo]" << std::endl;
d_s << "a = c" << std::endl;
d_s << "[bar bar]" << std::endl;
d_s << "a = d" << std::endl;
d_s << "[var]" << std::endl;
d_s << "b = x" << std::endl;
d_s << "a = ${a} ${b} ${c}" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_allow_sections },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(6, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("b", ff.get("a"));
EXPECT_EQ("d", ff.get("c"));
EXPECT_EQ("c", ff.get("foo/a"));
EXPECT_EQ("d", ff.get("bar/bar/a"));
EXPECT_EQ("x", ff.get("var/b"));
EXPECT_EQ("b x d", ff.get("var/a"));
}
TEST(KeyValueConfigFile, FancyAssigns)
{
std::stringstream d_s;
d_s << "a = A" << std::endl;
d_s << "a ?= X" << std::endl;
d_s << "b ?= B" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_allow_sections, kvcfo_allow_fancy_assigns },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(2, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("A", ff.get("a"));
EXPECT_EQ("B", ff.get("b"));
}
TEST(KeyValueConfigFile, EnvVars)
{
::setenv("A_VAR", "AAAARGH", 1);
::setenv("B_VAR", "BRRRRGH", 1);
std::stringstream d_s;
d_s << "a = ${ENV{A_VAR}}" << std::endl;
d_s << "b = ${ENV{B_VAR}}" << std::endl;
KeyValueConfigFile ff(d_s, { kvcfo_allow_env },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(2, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("AAAARGH", ff.get("a"));
EXPECT_EQ("BRRRRGH", ff.get("b"));
}
TEST(KeyValueConfigFile, AnnoyingLibtoolQuotes)
{
std::stringstream s1;
s1 << "x='foo 'bar' baz'\ny=z";
EXPECT_THROW(KeyValueConfigFile ff(s1, { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation), ConfigurationError);
std::stringstream s2;
s2 << "x='foo 'bar' baz'\ny=z";
KeyValueConfigFile ff(s2, { kvcfo_ignore_single_quotes_inside_strings },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(2, std::distance(ff.begin(), ff.end()));
EXPECT_EQ("foo 'bar' baz", ff.get("x"));
EXPECT_EQ("z", ff.get("y"));
std::stringstream s3;
s3 << "x='foo 'bar' baz'";
KeyValueConfigFile f3(s3, { kvcfo_ignore_single_quotes_inside_strings },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(1, std::distance(f3.begin(), f3.end()));
EXPECT_EQ("foo 'bar' baz", f3.get("x"));
std::stringstream s4;
s4 << "x='foo'";
KeyValueConfigFile f4(s4, { kvcfo_ignore_single_quotes_inside_strings },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
ASSERT_EQ(1, std::distance(f4.begin(), f4.end()));
EXPECT_EQ("foo", f4.get("x"));
}