[glom/import_csv_refactored] Import: Fixed tests to fit new parser API
- From: Michael Hasselmann <mikhas src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [glom/import_csv_refactored] Import: Fixed tests to fit new parser API
- Date: Fri, 25 Sep 2009 11:54:49 +0000 (UTC)
commit b3eca39111a4960eb14f42f302d459284b439d6b
Author: Michael Hasselmann <michaelh openismus com>
Date: Fri Sep 25 13:18:19 2009 +0200
Import: Fixed tests to fit new parser API
* glom/import_csv/csv_parser.[h|cc]: Added a finished_parsing signal since the
max_row count logic is flawed
(https://bugzilla.gnome.org/show_bug.cgi?id=588233#c16). Also moved common
parts of on_stream_read(.) (now on_buffer_read(.)) into
copy_buffer_and_continue_reading(.), to avoid copy'n'paste bugs.
* tests/import/utils.[h|cc]: Added mainloop functionality to trigger the idle
parse sequence of the CsvParser for the testcases.
* tests/import/test_[parsing,signals].cc: Fixed the tests to fit new parser
API.
glom/import_csv/csv_parser.cc | 169 ++++++++++++++++-------------------------
glom/import_csv/csv_parser.h | 25 ++++---
tests/import/test_parsing.cc | 147 +++++++++++++++++------------------
tests/import/test_signals.cc | 42 +++++------
tests/import/utils.cc | 65 +++++++++++++++-
tests/import/utils.h | 11 +++
6 files changed, 244 insertions(+), 215 deletions(-)
---
diff --git a/glom/import_csv/csv_parser.cc b/glom/import_csv/csv_parser.cc
index 19889f9..ccb1f5d 100644
--- a/glom/import_csv/csv_parser.cc
+++ b/glom/import_csv/csv_parser.cc
@@ -140,6 +140,11 @@ CsvParser::type_signal_encoding_error CsvParser::signal_encoding_error() const
return m_signal_encoding_error;
}
+CsvParser::type_signal_finished_parsing CsvParser::signal_finished_parsing() const
+{
+ return m_finished_parsing;
+}
+
CsvParser::type_signal_line_scanned CsvParser::signal_line_scanned() const
{
return m_signal_line_scanned;
@@ -236,8 +241,8 @@ void CsvParser::clear()
// Set to current encoding I guess ...
//m_conv("UTF-8", encoding),
m_input_position= 0;
- // Disconnect signal handlers, too? Nah, I don't think so ...
- //m_idle_connection.disconnect();
+ // Disconnect signal handlers, too.
+ m_idle_connection.disconnect();
m_line_number = 0;
set_state(STATE_NONE);
}
@@ -388,7 +393,9 @@ bool CsvParser::on_idle_parse()
}
// We have parsed the whole file. We have finished.
+ // TODO: To only emit signal_finished_parsing here is *not* enough.
set_state(STATE_PARSED);
+ signal_finished_parsing().emit();
}
// Continue if there are more bytes to process
@@ -424,132 +431,98 @@ void CsvParser::do_line_scanned(const Glib::ustring& line, guint line_number)
row.push_back(field);
}
- signal_line_scanned().emit(line, line_number);
+ signal_line_scanned().emit(row, line_number);
}
void CsvParser::on_file_read(const Glib::RefPtr<Gio::AsyncResult>& result)
{
+ // TODO: Introduce CsvParser::is_idle_handler_connected() instead?
+ if(!m_idle_connection.connected())
+ {
+ m_idle_connection = Glib::signal_idle().connect(sigc::mem_fun(*this, &CsvParser::on_idle_parse));
+ }
+
#ifdef GLIBMM_EXCEPTIONS_ENABLED
try
{
m_stream = m_file->read_finish(result);
m_buffer.reset(new Buffer);
- m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_stream_read));
+ m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_buffer_read));
}
- catch(const Glib::Exception& error)
+ catch(const Glib::Exception& ex)
{
- signal_file_read_error().emit( error.what() );
+ signal_file_read_error().emit(ex.what());
clear();
- // TODO: Response?
}
#else
- std::auto_ptr<Glib::Error> error;
- m_stream = m_file->read_finish(result, error);
- if (!error.get())
- {
- m_buffer.reset(new Buffer);
- m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_stream_read));
- }
- else
- {
- signal_file_read_error().emit( error->what() );
- clear();
- }
-#endif
+ std::auto_ptr<Glib::Error> error;
+ m_stream = m_file->read_finish(result, error);
+ if (!error.get())
+ {
+ m_buffer.reset(new Buffer);
+ m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_buffer_read));
+ }
+ else
+ {
+ signal_file_read_error().emit(error->what());
+ clear();
+ }
+#endif
}
+void CsvParser::copy_buffer_and_continue_reading(gssize size)
+{
+ if(size > 0)
+ {
+ m_raw.insert(m_raw.end(), m_buffer->buf, m_buffer->buf + size);
-void CsvParser::on_stream_read(const Glib::RefPtr<Gio::AsyncResult>& result)
+ m_buffer.reset(new Buffer);
+ m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_buffer_read));
+ }
+ else // When size == 0 we finished reading.
+ {
+ //TODO: put in proper data reset method?
+ m_buffer.reset(0);
+ m_stream.reset();
+ m_file.reset();
+ }
+}
+
+void CsvParser::on_buffer_read(const Glib::RefPtr<Gio::AsyncResult>& result)
{
#ifdef GLIBMM_EXCEPTIONS_ENABLED
try
{
const gssize size = m_stream->read_finish(result);
- m_raw.insert(m_raw.end(), m_buffer->buf, m_buffer->buf + size);
-
- // If the parser already exists, but it is currently not parsing because it waits
- // for new input, then continue parsing.
- // TODO: Introduce CsvParser::is_idle_handler_connected() instead?
- if(!m_idle_connection.connected())
- {
- m_idle_connection = Glib::signal_idle().connect(sigc::mem_fun(*this, &CsvParser::on_idle_parse));
- }
- // If the parser does not exist yet, then create a new parser, except when the
- // current encoding does not work for the file, in which case the user must first
- // choose another encoding.
- else if(m_state != CsvParser::STATE_ENCODING_ERROR)
- {
- begin_parse();
- }
-
- if(size > 0)
- {
- // Read the next few bytes
- m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_stream_read));
- }
- else
- {
- // Finished reading
- m_buffer.reset(0);
- m_stream.reset();
- m_file.reset();
- }
+ copy_buffer_and_continue_reading(size);
}
- catch(const Glib::Exception& error)
+ catch(const Glib::Exception& ex)
{
- signal_file_read_error().emit( error.what() );
+ signal_file_read_error().emit(ex.what());
clear();
- // TODO: Response?
}
#else
- std::auto_ptr<Glib::Error> error;
- const gssize size = m_stream->read_finish(result, error);
- if (!error.get())
- {
- m_raw.insert(m_raw.end(), m_buffer->buf, m_buffer->buf + size);
-
- // If the parser already exists, but it is currently not parsing because it waits
- // for new input, then continue parsing.
- if(!m_idle_connection.connected())
- {
- m_idle_connection = Glib::signal_idle().connect(sigc::mem_fun(*m_parser.get(), &CsvParser::on_idle_parse));
- }
- // If the parser does not exist yet, then create a new parser, except when the
- // current encoding does not work for the file ,in which case the user must first
- // choose another encoding.
- else if(m_state != CsvParser::ENCODING_ERROR)
- {
- begin_parse();
- }
-
- if(size > 0)
- {
- // Read the next few bytes
- m_stream->read_async(m_buffer->buf, sizeof(m_buffer->buf), sigc::mem_fun(*this, &CsvParser::on_stream_read));
- }
- else
- {
- // Finished reading
- m_buffer.reset(0);
- m_stream.reset();
- m_file.reset();
- }
- }
- if (error.get())
- {
- signal_file_read_error().emit( error->what() );
- clear();
- }
+ std::auto_ptr<Glib::Error> error;
+ const gssize size = m_stream->read_finish(result, error);
+ if (!error.get())
+ {
+ copy_buffer_and_continue_reading(size)
+ }
+ else
+ {
+ signal_file_read_error().emit(error->what());
+ clear();
+ }
#endif
}
-
void CsvParser::on_file_query_info(const Glib::RefPtr<Gio::AsyncResult>& result)
{
#ifdef GLIBMM_EXCEPTIONS_ENABLED
try
{
+ // Why is m_file null? Did we clear the parser before reading the file info?
Glib::RefPtr<Gio::FileInfo> info = m_file->query_info_finish(result);
if(info)
signal_have_display_name().emit(info->get_display_name());
@@ -568,17 +541,7 @@ void CsvParser::on_file_query_info(const Glib::RefPtr<Gio::AsyncResult>& result)
}
else
std::cerr << "Failed to fetch display name of uri " << m_file->get_uri() << ": " << error->what() << std::endl;
-#endif
-}
-
-
-//TODO This seems to be superfluous - we already connect the idle handler elsewhere.
-void CsvParser::begin_parse()
-{
- clear();
-
- set_state(STATE_PARSING);
- m_idle_connection = Glib::signal_idle().connect(sigc::mem_fun(*this, &CsvParser::on_idle_parse));
+#endif
}
void CsvParser::set_state(State state)
diff --git a/glom/import_csv/csv_parser.h b/glom/import_csv/csv_parser.h
index 41f77e3..93554aa 100644
--- a/glom/import_csv/csv_parser.h
+++ b/glom/import_csv/csv_parser.h
@@ -48,6 +48,9 @@ class CsvParser
{
public:
+ typedef std::vector<Glib::ustring> type_row_strings;
+ typedef std::vector<type_row_strings> type_rows;
+
//TODO: Avoid having to specify an initial encoding.
explicit CsvParser(const std::string& encoding_charset);
@@ -99,13 +102,20 @@ public:
type_signal_encoding_error signal_encoding_error() const;
- typedef sigc::signal<void, std::string, unsigned int> type_signal_line_scanned;
+ typedef sigc::signal<void, type_row_strings, unsigned int> type_signal_line_scanned;
/** This signal will be emitted each time the parser has scanned a line. TODO: Do we mean row instead of line? - A row contain a newline.
*/
type_signal_line_scanned signal_line_scanned() const;
+ typedef sigc::signal<void> type_signal_finished_parsing;
+
+ /** This signal will be emitted when the parser successfully finished to parse a file.
+ */
+ type_signal_finished_parsing signal_finished_parsing() const;
+
+
typedef sigc::signal<void> type_signal_state_changed;
/** This signal will be emitted when the state changes.
@@ -126,17 +136,10 @@ public:
void set_file_and_start_parsing(const std::string& uri);
private:
-
- typedef std::vector<Glib::ustring> type_row_strings;
- typedef std::vector<type_row_strings> type_rows;
-
-
// In order to not make the UI feel sluggish during larger imports we parse
// on chunk at a time in the idle handler.
-public: // public because it is needed for testing => no main loop
bool on_idle_parse();
-private:
void begin_parse();
static const gunichar DELIMITER = ',';
@@ -147,12 +150,11 @@ private:
void do_line_scanned(const Glib::ustring& current_line, guint line_number);
//TODO: Document this:
-public: // public because it is needed for testing => no main loop
static Glib::ustring::const_iterator advance_field(const Glib::ustring::const_iterator& iter, const Glib::ustring::const_iterator& end, Glib::ustring& field);
-private:
void on_file_read(const Glib::RefPtr<Gio::AsyncResult>& result);
- void on_stream_read(const Glib::RefPtr<Gio::AsyncResult>& result);
+ void copy_buffer_and_continue_reading(gssize size);
+ void on_buffer_read(const Glib::RefPtr<Gio::AsyncResult>& result);
void on_file_query_info(const Glib::RefPtr<Gio::AsyncResult>& result);
void set_state(State state);
@@ -180,6 +182,7 @@ private:
type_signal_have_display_name m_signal_have_display_name;
type_signal_encoding_error m_signal_encoding_error;
type_signal_line_scanned m_signal_line_scanned;
+ type_signal_finished_parsing m_finished_parsing;
type_signal_state_changed m_signal_state_changed;
Glib::RefPtr<Gio::File> m_file;
diff --git a/tests/import/test_parsing.cc b/tests/import/test_parsing.cc
index 733acbd..b538232 100644
--- a/tests/import/test_parsing.cc
+++ b/tests/import/test_parsing.cc
@@ -8,22 +8,32 @@
namespace
{
+void print_tokens();
+
typedef std::vector<std::string> type_tokens;
type_tokens& get_tokens_instance()
{
- static type_tokens tokens;
- return tokens;
+ static type_tokens type_tokens;
+ return type_tokens;
}
-void on_line_scanned(const Glib::ustring& line, guint line_number);
+void on_line_scanned(const std::vector<Glib::ustring>& row, guint /*line_number*/)
+{
+ for(std::vector<Glib::ustring>::const_iterator iter = row.begin();
+ iter != row.end();
+ ++iter)
+ {
+ get_tokens_instance().push_back(*iter);
+ }
+}
void print_tokens()
{
for(type_tokens::const_iterator iter = get_tokens_instance().begin();
- iter != get_tokens_instance().end();
- ++iter)
+ iter != get_tokens_instance().end();
+ ++iter)
{
std::cout << " [" << *iter << "] ";
}
@@ -34,7 +44,7 @@ void print_tokens()
bool check_tokens(const std::string& regex)
{
Glib::RefPtr<Glib::Regex> check;
-
+
#ifdef GLIBMM_EXCEPTIONS_ENABLED
try
{
@@ -44,7 +54,7 @@ bool check_tokens(const std::string& regex)
{
std::cerr << "Glib::Regex::create() failed: " << ex.what() << std::endl;
return false;
- }
+ }
#else
std::auto_ptr<Glib::Error> ex;
check = Glib::Regex::create(regex, static_cast<Glib::RegexCompileFlags>(0), static_cast<Glib::RegexMatchFlags>(0), ex);
@@ -54,10 +64,10 @@ bool check_tokens(const std::string& regex)
return false;
}
#endif
-
- if(!check)
+
+ if(!check && 0 == get_tokens_instance().size())
return false;
-
+
for(type_tokens::const_iterator iter = get_tokens_instance().begin();
iter != get_tokens_instance().end();
++iter)
@@ -69,23 +79,9 @@ bool check_tokens(const std::string& regex)
return true;
}
-void on_line_scanned(const Glib::ustring& line, guint /*line_number*/)
+void connect_signals(Glom::CsvParser& parser)
{
- Glib::ustring field;
- Glib::ustring::const_iterator line_iter(line.begin());
-
- while(line_iter != line.end())
- {
- line_iter = Glom::CsvParser::advance_field(line_iter, line.end(), field);
- get_tokens_instance().push_back(field);
-
- // Manually have to skip separators.
- if(',' == *line_iter)
- {
- get_tokens_instance().push_back(",");
- ++line_iter;
- }
- }
+ parser.signal_line_scanned().connect(sigc::ptr_fun(&on_line_scanned));
}
} // namespace
@@ -93,110 +89,109 @@ void on_line_scanned(const Glib::ustring& line, guint /*line_number*/)
// Testcases
int main(int argc, char* argv[])
{
+ Glib::thread_init();
Gtk::Main gtk(argc, argv);
- Glom::CsvParser parser("UTF-8");
- parser.signal_line_scanned().connect(sigc::ptr_fun(&on_line_scanned));
-
bool result = true;
std::stringstream report;
// test_dquoted_string
{
- const char raw_line[] = "\"a \"\"quoted\"\" token\",\"sans quotes\"\n";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "\"a \"\"quoted\"\" token\",\"sans quotes\"\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- parser.set_file_and_start_parsing("./tests/import/data/dquoted_string.csv");
-
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("^(a \"quoted\" token|sans quotes)$") &&
+ 2 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = check_tokens("^(a \"quoted\" token|,|sans quotes)$");
if(!ImportTests::check("test_dquoted_string", passed, report))
result = false;
-
- get_tokens_instance().clear();
- parser.clear();
}
// test_skip_on_no_ending_newline
{
- const char raw_line[] = "\"this\",\"line\",\"will\",\"be\",\"skipped\"";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "\"token in first line\"\n\"2nd token\", \"but\", \"this\",\"line\",\"will\",\"be\",\"skipped\"";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("token in first line") &&
+ 1 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = (get_tokens_instance().size() == 0);
if(!ImportTests::check("test_skip_on_no_ending_newline", passed, report))
result = false;
-
- get_tokens_instance().clear();
- parser.clear();
}
// test_skip_on_no_quotes_around_token
{
- const char raw_line[] = "this,line,contains,only,empty,tokens\n";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "this,line,contains,only,empty,tokens\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("^$") &&
+ 6 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = check_tokens("^(|,)$");
if(!ImportTests::check("test_skip_on_no_quotes_around_token", passed, report))
result = false;
-
- get_tokens_instance().clear();
- parser.clear();
}
// test_skip_spaces_around_separators
{
- const char raw_line[] = "\"spaces\" , \"around\", \"separators\"\n";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "\"spaces\" , \"around\", \"separators\"\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("^(spaces|around|separators)$") &&
+ 3 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = (get_tokens_instance().size() == 5);
if(!ImportTests::check("test_skip_spaces_around_separators", passed, report))
result = false;
- get_tokens_instance().clear();
- parser.clear();
}
// test_fail_on_non_comma_separators
{
- const char raw_line[] = "\"cannot\"\t\"tokenize\"\t\"this\"\n";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "\"cannot\"\t\"tokenize\"\t\"this\"\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("^cannottokenizethis$") &&
+ 1 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = check_tokens("^cannottokenizethis$");
if(!ImportTests::check("test_fail_on_non_comma_separators", passed, report))
result = false;
-
- get_tokens_instance().clear();
- parser.clear();
}
// test_parse_newline_inside_quotes
{
- const char raw_line[] = "\"cell with\nnewline\"\n\"token on next line\"";
- ImportTests::set_parser_contents(parser, raw_line, sizeof(raw_line));
+ const char raw[] = "\"cell with\nnewline\"\n\"token on next line\"\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
+ bool passed = (finished_parsing &&
+ check_tokens("^(cell with\nnewline|token on next line)$") &&
+ 2 == get_tokens_instance().size());
+ get_tokens_instance().clear();
- bool passed = check_tokens("^(cell with\nnewline|token on next line)$");
if(!ImportTests::check("test_parse_newline_inside_quotes", passed, report))
result = false;
+ }
+ // test_fail_on_non_matching_quotes
+ {
+ const char raw[] = "\"token\"\nthis quote has no partner\",\"token\"\n";
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
+
+ bool passed = (finished_parsing &&
+ check_tokens("token") &&
+ 1 == get_tokens_instance().size());
get_tokens_instance().clear();
- parser.clear();
+
+ if(!ImportTests::check("test_fail_on_non_matching_quotes", passed, report))
+ result = false;
}
if(!result)
diff --git a/tests/import/test_signals.cc b/tests/import/test_signals.cc
index 25ddbe7..b269201 100644
--- a/tests/import/test_signals.cc
+++ b/tests/import/test_signals.cc
@@ -44,17 +44,20 @@ void print_signal_counts()
std::cout << "encoding errors: " << get_encoding_error_count_instance() << std::endl;
}
+void connect_signals(Glom::CsvParser& parser)
+{
+ parser.signal_line_scanned().connect(sigc::hide(sigc::hide(&on_line_scanned)));
+ parser.signal_encoding_error().connect(sigc::ptr_fun(&on_encoding_error));
+}
+
} // namespace
// Testcases
int main(int argc, char* argv[])
{
+ Glib::thread_init();
Gtk::Main gtk(argc, argv);
- Glom::CsvParser parser("UTF-8");
- parser.signal_line_scanned().connect(sigc::hide(sigc::hide(&on_line_scanned)));
- parser.signal_encoding_error().connect(sigc::ptr_fun(&on_encoding_error));
-
bool result = true;
std::stringstream report;
@@ -62,41 +65,36 @@ int main(int argc, char* argv[])
{
// 2 CSV lines, first one contains newlines inside quotes
const char raw[] = "\"some\n quoted\r\n newlines\n\", \"token2\"\n\"token3\"\n";
- ImportTests::set_parser_contents(parser, raw, sizeof(raw));
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
-
- bool passed = (2 == get_line_scanned_count_instance() &&
+ bool passed = (finished_parsing &&
+ 2 == get_line_scanned_count_instance() &&
0 == get_encoding_error_count_instance());
if(!ImportTests::check("test_ignore_quoted_newlines", passed, report))
result = false;
reset_signal_counts();
- parser.clear();
}
// test_ignore_empty_lines
{
// 5 CSV lines, but only 2 contain data
const char raw[] = "token1\n\n\n\ntoken2, token3\n";
- ImportTests::set_parser_contents(parser, raw, sizeof(raw));
-
- while(parser.on_idle_parse())
- {}
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- const bool passed = (2 == get_line_scanned_count_instance() &&
+ bool passed = (finished_parsing &&
+ 2 == get_line_scanned_count_instance() &&
0 == get_encoding_error_count_instance());
if(!ImportTests::check("test_ignore_empty_lines", passed, report))
result = false;
reset_signal_counts();
- parser.clear();
}
- // test_wrong_encoding
+// TODO: Cannot currently run this test in a sane fashion, fix me!
+/*
{
const char* const encoding_arr[] = {"UTF-8", "UCS-2"};
type_encodings encodings(encoding_arr, encoding_arr + G_N_ELEMENTS(encoding_arr));
@@ -141,24 +139,22 @@ int main(int argc, char* argv[])
reset_signal_counts();
parser.clear();
}
+*/
// test_incomplete_chars
{
// An incomplete Unicode sequence.
const char raw[] = "\0xc0\n";
- ImportTests::set_parser_contents(parser, raw, sizeof(raw));
+ bool finished_parsing = ImportTests::run_parser_from_buffer(&connect_signals, raw, sizeof(raw));
- while(parser.on_idle_parse())
- {}
-
- const bool passed = (1 == get_encoding_error_count_instance() &&
+ bool passed = (finished_parsing &&
+ 1 == get_encoding_error_count_instance() &&
0 == get_line_scanned_count_instance());
if(!ImportTests::check("test_incomplete_chars", passed, report))
result = false;
reset_signal_counts();
- parser.clear();
}
if(!result)
diff --git a/tests/import/utils.cc b/tests/import/utils.cc
index 16de7af..2f3abd4 100644
--- a/tests/import/utils.cc
+++ b/tests/import/utils.cc
@@ -13,8 +13,69 @@ bool check(const std::string& name, bool test, std::stringstream& report)
void set_parser_contents(Glom::CsvParser& /*parser*/, const char* /*input*/, guint /*size*/)
{
- // Do not read terminating null byte.
- //parser.m_raw = std::vector<char>(input, input + size -1);
+}
+
+std::string create_file_from_buffer(const char* input, guint size)
+{
+ // use Glib's file utilities to get an unique tmp filename
+ std::string tmp_filename;
+ int tmp_file_handle = Glib::file_open_tmp(tmp_filename, "glom_testdata");
+
+ if (0 < tmp_file_handle)
+ {
+ ssize_t result = write(tmp_file_handle, input, size);
+ g_assert(-1 != result); // g_return_with_val would still be wrong here, I think?
+
+ close(tmp_file_handle);
+ }
+
+ return Glib::filename_to_uri(tmp_filename);
+}
+
+Glib::RefPtr<Glib::MainLoop>& get_mainloop_instance()
+{
+ static Glib::RefPtr<Glib::MainLoop> mainloop(0);
+ return mainloop;
+}
+
+bool& get_result_instance()
+{
+ static bool result = true;
+ return result;
+}
+
+void on_mainloop_killed_by_watchdog()
+{
+ get_result_instance() = false; // Comment out if you want to run tests and get useful results even with a non-working finished_parsing signal.
+ get_mainloop_instance()->quit();
+}
+
+bool run_parser_from_buffer(void (*connect_parser_signals)(Glom::CsvParser& parser), const char* input, guint size)
+{
+ get_result_instance() = true;
+
+ get_mainloop_instance().reset();
+ get_mainloop_instance() = Glib::MainLoop::create();
+
+ Glom::CsvParser parser("UTF-8");
+
+ parser.signal_finished_parsing().connect(sigc::mem_fun(*get_mainloop_instance().operator->(), &Glib::MainLoop::quit));
+ // Install a watchdog for the mainloop, no test should need longer than 3
+ // seconds. Also, we need to guard against being stuck in the mainloop.
+ // Infinitely running tests are useless.
+ get_mainloop_instance()->get_context()->signal_timeout().connect_seconds_once(sigc::ptr_fun(&on_mainloop_killed_by_watchdog), 3);
+
+ (*connect_parser_signals)(parser);
+
+ const std::string file_name = create_file_from_buffer(input, size);
+ parser.set_file_and_start_parsing(file_name);
+
+ get_mainloop_instance()->run();
+
+ int result = unlink(Glib::filename_from_uri(file_name).c_str());
+ g_assert(-1 != result);
+
+ return get_result_instance();
}
} //namespace ImportTests
diff --git a/tests/import/utils.h b/tests/import/utils.h
index 778dc1d..76cc14a 100644
--- a/tests/import/utils.h
+++ b/tests/import/utils.h
@@ -3,6 +3,8 @@
#include <glom/import_csv/csv_parser.h>
#include <iostream>
+#include <unistd.h>
+#include <errno.h>
namespace ImportTests
{
@@ -12,5 +14,14 @@ bool check(const std::string& name, bool test, std::stringstream& report);
/// This takes a @a size argument so we can test parsing of null bytes.
void set_parser_contents(Glom::CsvParser& parser, const char* input, guint size);
+// Returns the file name of the temporary created file, which will contain the buffer's contents.
+std::string create_file_from_buffer(const char* input, guint size);
+
+bool run_parser_from_buffer(void (*connect_parser_signals)(Glom::CsvParser& parser), const char* input, guint size);
+
+void on_mainloop_killed_by_watchdog();
+Glib::RefPtr<Glib::MainLoop>& get_mainloop_instance();
+bool& get_result_instance();
+
} //namespace ImportTests
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]