add strToNum test driver for PStringUtils
Stand-alone test under src/tests/strToNum exercising every PStringUtils method (Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase / ContainsNoCase / BeginsWithNoCase). Three modes: built-in pass/fail suite, ad-hoc inspection of command-line strings, and an interactive prompt. Adds -h/--help and -i/--interactive options. Pure C++17, no ROOT dependency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
/***************************************************************************
|
||||
|
||||
strToNum.cpp
|
||||
|
||||
Author: Andreas Suter
|
||||
e-mail: andreas.suter@psi.ch
|
||||
|
||||
Little stand-alone test driver for the PStringUtils class. It exercises
|
||||
Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /
|
||||
ContainsNoCase / BeginsWithNoCase and reports a pass/fail summary.
|
||||
|
||||
Usage:
|
||||
strToNum -> run the built-in test suite
|
||||
strToNum <string> -> show what PStringUtils makes of the given
|
||||
string(s) on the command line (ad-hoc checks)
|
||||
strToNum -i -> interactive mode: type a string at the prompt
|
||||
and see the result of every PStringUtils method
|
||||
(empty line or 'quit' to leave)
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2007-2026 by Andreas Suter *
|
||||
* andreas.suter@psi.ch *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program 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 <cmath>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "PStringUtils.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// tiny test bookkeeping
|
||||
//--------------------------------------------------------------------------
|
||||
static int gPassed = 0;
|
||||
static int gFailed = 0;
|
||||
|
||||
static void check(const std::string &what, bool ok)
|
||||
{
|
||||
if (ok) {
|
||||
++gPassed;
|
||||
std::cout << " [ ok ] " << what << std::endl;
|
||||
} else {
|
||||
++gFailed;
|
||||
std::cout << " [FAIL] " << what << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static std::string vecToStr(const std::vector<std::string> &v)
|
||||
{
|
||||
std::string s = "{";
|
||||
for (std::vector<std::string>::size_type i = 0; i < v.size(); ++i) {
|
||||
s += "'" + v[i] + "'";
|
||||
if (i + 1 < v.size())
|
||||
s += ", ";
|
||||
}
|
||||
s += "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testSplit()
|
||||
{
|
||||
std::cout << "Split:" << std::endl;
|
||||
|
||||
std::vector<std::string> r = PStringUtils::Split("alpha beta gamma", " ");
|
||||
std::cout << " input: 'alpha beta gamma', delim: ' '" << std::endl;
|
||||
check("3 tokens on single space delimiter",
|
||||
r.size() == 3 && r[0] == "alpha" && r[1] == "beta" && r[2] == "gamma");
|
||||
std::cout << "----" << std::endl;
|
||||
r = PStringUtils::Split(" , 1 , 2 ,, 3 ,", " ,");
|
||||
std::cout << " input: ' , 1 , 2 ,, 3 ,', delim: ' ,'" << std::endl;
|
||||
std::cout << " -> ";
|
||||
for (auto i: r)
|
||||
std::cout << i << ", ";
|
||||
std::cout << std::endl;
|
||||
check("mixed/repeated delimiters, empty tokens skipped",
|
||||
r.size() == 3 && r[0] == "1" && r[1] == "2" && r[2] == "3");
|
||||
std::cout << "----" << std::endl;
|
||||
|
||||
r = PStringUtils::Split("", " ");
|
||||
check("empty input -> no tokens", r.empty());
|
||||
|
||||
r = PStringUtils::Split(" ", " ");
|
||||
check("delimiters only -> no tokens", r.empty());
|
||||
|
||||
r = PStringUtils::Split("nodelim", ",;");
|
||||
check("no delimiter present -> single token",
|
||||
r.size() == 1 && r[0] == "nodelim");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testIsInt()
|
||||
{
|
||||
std::cout << "IsInt:" << std::endl;
|
||||
|
||||
check("'12345' is int", PStringUtils::IsInt("12345"));
|
||||
check("' 42 ' (surrounding ws) is int", PStringUtils::IsInt(" 42 "));
|
||||
check("'' is not int", !PStringUtils::IsInt(""));
|
||||
check("' ' (ws only) is not int", !PStringUtils::IsInt(" "));
|
||||
check("'-5' is not int (sign not allowed)", !PStringUtils::IsInt("-5"));
|
||||
check("'3.14' is not int", !PStringUtils::IsInt("3.14"));
|
||||
check("'12a' is not int", !PStringUtils::IsInt("12a"));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testIsFloat()
|
||||
{
|
||||
std::cout << "IsFloat:" << std::endl;
|
||||
|
||||
check("'3.14' is float", PStringUtils::IsFloat("3.14"));
|
||||
check("'-1.2e-3' is float", PStringUtils::IsFloat("-1.2e-3"));
|
||||
check("'+42' is float", PStringUtils::IsFloat("+42"));
|
||||
check("'.5' is float", PStringUtils::IsFloat(".5"));
|
||||
check("' 6.022e23 ' (surrounding ws) is float",
|
||||
PStringUtils::IsFloat(" 6.022e23 "));
|
||||
check("'' is not float", !PStringUtils::IsFloat(""));
|
||||
check("'nan' is not float", !PStringUtils::IsFloat("nan"));
|
||||
check("'inf' is not float", !PStringUtils::IsFloat("inf"));
|
||||
check("'1.2.3' is not float", !PStringUtils::IsFloat("1.2.3"));
|
||||
check("'12abc' is not float", !PStringUtils::IsFloat("12abc"));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testToInt()
|
||||
{
|
||||
std::cout << "ToInt:" << std::endl;
|
||||
|
||||
bool ok = false;
|
||||
check("'42' -> 42, ok", PStringUtils::ToInt("42", &ok) == 42 && ok);
|
||||
check("' -7' -> -7, ok", PStringUtils::ToInt(" -7", &ok) == -7 && ok);
|
||||
check("'123abc' -> 123, ok (trailing ignored)",
|
||||
PStringUtils::ToInt("123abc", &ok) == 123 && ok);
|
||||
check("'abc' -> 0, !ok", PStringUtils::ToInt("abc", &ok) == 0 && !ok);
|
||||
check("'' -> 0, !ok", PStringUtils::ToInt("", &ok) == 0 && !ok);
|
||||
check("'99999999999999999999' -> out of range, !ok",
|
||||
(PStringUtils::ToInt("99999999999999999999", &ok), !ok));
|
||||
check("null ok pointer is tolerated", PStringUtils::ToInt("17") == 17);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testToDouble()
|
||||
{
|
||||
std::cout << "ToDouble:" << std::endl;
|
||||
|
||||
bool ok = false;
|
||||
check("'3.14' -> 3.14, ok",
|
||||
std::fabs(PStringUtils::ToDouble("3.14", &ok) - 3.14) < 1e-12 && ok);
|
||||
check("' +1.5e2' -> 150, ok",
|
||||
std::fabs(PStringUtils::ToDouble(" +1.5e2", &ok) - 150.0) < 1e-9 && ok);
|
||||
check("'2.5xyz' -> 2.5, ok (trailing ignored)",
|
||||
std::fabs(PStringUtils::ToDouble("2.5xyz", &ok) - 2.5) < 1e-12 && ok);
|
||||
check("'abc' -> 0.0, !ok", PStringUtils::ToDouble("abc", &ok) == 0.0 && !ok);
|
||||
check("'' -> 0.0, !ok", PStringUtils::ToDouble("", &ok) == 0.0 && !ok);
|
||||
check("'1e400' -> out of range, !ok",
|
||||
(PStringUtils::ToDouble("1e400", &ok), !ok));
|
||||
check("null ok pointer is tolerated",
|
||||
std::fabs(PStringUtils::ToDouble("0.25") - 0.25) < 1e-12);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void testCaseHelpers()
|
||||
{
|
||||
std::cout << "IsEqualNoCase / ContainsNoCase / BeginsWithNoCase:" << std::endl;
|
||||
|
||||
check("'Fit' == 'fIT' (no case)", PStringUtils::IsEqualNoCase("Fit", "fIT"));
|
||||
check("'Fit' != 'Fits'", !PStringUtils::IsEqualNoCase("Fit", "Fits"));
|
||||
check("'' == ''", PStringUtils::IsEqualNoCase("", ""));
|
||||
|
||||
check("'Hello World' contains 'LO WO' (no case)",
|
||||
PStringUtils::ContainsNoCase("Hello World", "LO WO"));
|
||||
check("empty needle is contained",
|
||||
PStringUtils::ContainsNoCase("abc", ""));
|
||||
check("needle longer than haystack is not contained",
|
||||
!PStringUtils::ContainsNoCase("ab", "abc"));
|
||||
|
||||
check("'THEORY' begins with 'the' (no case)",
|
||||
PStringUtils::BeginsWithNoCase("THEORY", "the"));
|
||||
check("'THEORY' does not begin with 'ory'",
|
||||
!PStringUtils::BeginsWithNoCase("THEORY", "ory"));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void inspect(const std::string &str)
|
||||
{
|
||||
std::cout << "Inspecting '" << str << "':" << std::endl;
|
||||
std::cout << " Split(' \\t,;') = "
|
||||
<< vecToStr(PStringUtils::Split(str, " \t,;")) << std::endl;
|
||||
std::cout << " IsInt = " << (PStringUtils::IsInt(str) ? "true" : "false") << std::endl;
|
||||
std::cout << " IsFloat = " << (PStringUtils::IsFloat(str) ? "true" : "false") << std::endl;
|
||||
|
||||
bool ok = false;
|
||||
int i = PStringUtils::ToInt(str, &ok);
|
||||
std::cout << " ToInt = " << i << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
|
||||
double d = PStringUtils::ToDouble(str, &ok);
|
||||
std::cout << " ToDouble = " << d << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
std::cout
|
||||
<< "usage: " << prog << " [options] [string ...]\n"
|
||||
<< "\n"
|
||||
<< "Little stand-alone test driver for the PStringUtils class. It exercises\n"
|
||||
<< "Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /\n"
|
||||
<< "ContainsNoCase / BeginsWithNoCase.\n"
|
||||
<< "\n"
|
||||
<< "options:\n"
|
||||
<< " -h, --help show this help and exit\n"
|
||||
<< " -i, --interactive interactive mode: type a string at the prompt and\n"
|
||||
<< " see the result of every PStringUtils method\n"
|
||||
<< " (empty line, 'quit'/'exit' or Ctrl-D leaves)\n"
|
||||
<< "\n"
|
||||
<< "arguments:\n"
|
||||
<< " string ... one or more strings to inspect; the result of every\n"
|
||||
<< " PStringUtils method is printed for each of them\n"
|
||||
<< "\n"
|
||||
<< "with no options and no arguments the built-in test suite is run; the exit\n"
|
||||
<< "code is 0 if all checks pass and 1 otherwise.\n"
|
||||
<< "\n"
|
||||
<< "examples:\n"
|
||||
<< " " << prog << " run the built-in test suite\n"
|
||||
<< " " << prog << " \" -42xyz\" 1.5e-3 inspect the given strings\n"
|
||||
<< " " << prog << " -i interactive mode\n";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void interactive()
|
||||
{
|
||||
std::cout << "==== PStringUtils interactive mode ====" << std::endl;
|
||||
std::cout << "Enter a string to test the methods on it." << std::endl;
|
||||
std::cout << "An empty line or 'quit' leaves." << std::endl << std::endl;
|
||||
|
||||
std::string line;
|
||||
while (true) {
|
||||
std::cout << "strToNum> " << std::flush;
|
||||
if (!std::getline(std::cin, line)) // EOF (e.g. Ctrl-D)
|
||||
break;
|
||||
if (line.empty() || line == "quit" || line == "exit")
|
||||
break;
|
||||
inspect(line);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1) {
|
||||
const std::string arg1 = argv[1];
|
||||
if (arg1 == "-h" || arg1 == "--help") {
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (arg1 == "-i" || arg1 == "--interactive") {
|
||||
interactive();
|
||||
return 0;
|
||||
}
|
||||
// ad-hoc mode: inspect the strings given on the command line
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
inspect(argv[i]);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "==== PStringUtils test suite ====" << std::endl << std::endl;
|
||||
|
||||
testSplit();
|
||||
testIsInt();
|
||||
testIsFloat();
|
||||
testToInt();
|
||||
testToDouble();
|
||||
testCaseHelpers();
|
||||
|
||||
std::cout << std::endl
|
||||
<< "==== summary: " << gPassed << " passed, "
|
||||
<< gFailed << " failed ====" << std::endl;
|
||||
|
||||
return (gFailed == 0) ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user