FileNode* FileBot::constructParentPath(boost::filesystem::path inputPath)
{
...
...
typedef split_iterator<string::iterator> string_split_iterator;
for( string_split_iterator it = make_split_iterator(path, first_finder(GENERIC_PATH_SEPARATOR, is_equal()));
it != string_split_iterator();
++it)
{
FileNode *node = new FileNode();
node->setType('d');
node->setName(copy_range<string>(*it));
BOOST_LOG_TRIVIAL(info) << "value inserted: [" << copy_range<string>(*it) << "]";
if( parent == NULL )
fileList.push_back(*node);
else {
parent->sibling.push_back(*node);
node->setParentNode(parent);
}
parent = node;
...
...
}
}
This mistake would not cause any major crash, it just doesn't look "pretty" in terms of behavioral design. Before I go into the details, let's consider following 2 possible inputs:- /home/user
- C:/home/user
But entry 1 would become like this, the first node in the link-list doesn't contain any name.
Why this could happen? I though the code will automatically handle the first token when it is empty? And I also realized this only happened to Linux due to Linux file system structure design behave differently from Windows. To prove my assumption, I run a series of test on make_split_iterator(), and the result shows the method does return an empty string. Another test on boost::split() also having this issue. The only one success is boost::tokenizer().
BOOST_AUTO_TEST_CASE(TL_split_words, *boost::unit_test::precondition(skipTest(false)) )
{
string win = "D:/home/user";
string lin = "/home/user";
vector<string> words;
string str, firstElement;
cout << "***** version 1 *****" << endl;
cout << "Windows platform: ";
str = win;
boost::split(words, str, boost::is_any_of(GENERIC_PATH_SEPARATOR), boost::token_compress_on);
for(string w : words) {
cout << "[" << w << "]";
}
cout << endl;
firstElement = words.at(0);
BOOST_TEST(words.size() == 4);
BOOST_TEST(firstElement.compare("D:") == 0);
words.clear();
str = lin;
boost::split(words, str, boost::is_any_of(GENERIC_PATH_SEPARATOR), boost::token_compress_on);
cout << "Linux platform: ";
for(string w : words) {
cout << "[" << w << "]";
}
cout << endl;
firstElement = words.at(0);
BOOST_TEST(words.size() == 4);
BOOST_TEST(firstElement.compare("") == 0);
cout << "***** version 2 *****" << endl;
cout << "Windows platform: ";
str = win;
typedef split_iterator< string::iterator> string_split_iterator;
for( string_split_iterator it = make_split_iterator(str, first_finder(GENERIC_PATH_SEPARATOR, is_equal()));
it != string_split_iterator();
++it)
{
cout << "[" << copy_range<string>(*it) << "]";
}
cout << endl;
cout << "Linux platform: ";
str = lin;
typedef split_iterator<string::iterator> string_split_iterator;
for( string_split_iterator it = make_split_iterator(str, first_finder(GENERIC_PATH_SEPARATOR, is_equal()));
it != string_split_iterator();
++it)
{
cout << "[" << copy_range<string>(*it) << "]";
}
cout << endl;
cout << "***** version 3 *****" << endl;
cout << "Windows platform: ";
str = win;
boost::char_separator<char> sep{GENERIC_PATH_SEPARATOR.c_str()};
boost::tokenizer<boost::char_separator<char> > token{str, sep};
for( const auto &t : token ) {
cout << "[" << t << "]";
}
cout << endl;
cout << "Linux platform: ";
str = lin;
token = {str, sep};
for( const auto &t : token ) {
cout << "[" << t << "]";
}
cout << endl;
}
The output of this test would be like this:***** version 1 ***** Windows platform: [D:][home][user] Linux platform: [][home][user] ***** version 2 ***** Windows platform: [D:][home][user] Linux platform: [][home][user] ***** version 3 ***** Windows platform: [D:][home][user] Linux platform: [home][user]I admit that I have overlooked the code, it is just too detail which something I have missed. Although the test doesn't fail, but the consequent impact would cause the link-list contain a node without a name.

No comments:
Post a Comment