Improvements for markdown parsing.

This commit is contained in:
James Grogan 2022-12-06 18:02:43 +00:00
parent fc44290e3f
commit 8705859115
40 changed files with 957 additions and 537 deletions

View file

@ -1,5 +1,6 @@
set(COMPILER_UNIT_TEST_FILES
compiler/TestTemplatingEngine.cpp
compiler/TestLexer.cpp
PARENT_SCOPE
)

View file

@ -0,0 +1,19 @@
#include "Lexer.h"
#include "TestFramework.h"
#include "TestUtils.h"
#include <iostream>
TEST_CASE(TestLexer_MatchPattern, "[compiler]")
{
std::string input = "[I'm inside the tag](I'm inside the brackets), followed by more text.";
std::string pattern = "[@](@)";
std::vector<std::string> hits;
const auto matched = Lexer::matchPattern(pattern, input, '@', hits);
REQUIRE(matched);
REQUIRE(hits.size() == 2);
REQUIRE(hits[0] == "I'm inside the tag");
REQUIRE(hits[1] == "I'm inside the brackets");
}

View file

@ -3,13 +3,28 @@
#include "TestFramework.h"
#include "TestUtils.h"
#include <iostream>
TEST_CASE(TestStringUtils_Strip, "core")
TEST_CASE(TestStringUtils_StripSurroundingWhitepsace, "core")
{
std::string input = " super() ";
std::string stripped = StringUtils::strip(input);
auto predicate = stripped == "super()";
REQUIRE(predicate);
std::string stripped = StringUtils::stripSurroundingWhitepsace(input);
REQUIRE(stripped == "super()");
}
TEST_CASE(TestStringUtils_RemoveUpTo, "core")
{
std::string input = "def{filename}abc/123/456";
std::string removed = StringUtils::removeUpTo(input, "{filename}");
REQUIRE(removed == "abc/123/456");
}
TEST_CASE(TestStringUtils_startsWith, "core")
{
std::string input = " ```some triple ticks ";
bool ignore_whitespace{false};
auto starts_with = StringUtils::startsWith(input, "```", ignore_whitespace);
REQUIRE(!starts_with);
ignore_whitespace = true;
starts_with = StringUtils::startsWith(input, "```", ignore_whitespace);
REQUIRE(starts_with);
}

View file

@ -0,0 +1,30 @@
# I'm a level one header
I'm some text under level one
## I'm a level two header
I'm some text under level two
```
I'm a code block
```
I'm a line under the code block, with some `inline code`.
### I'm a level three header
I'm a bullet point list:
* First point
* Second point
* Third point
With a [hyperlink](www.imahyperlink.com) embedded.
# I'm another level one header
I'm some inline math $a = b + c$ and I'm some standalone math:
$$
d = e + f
$$
![This is an image](https://myoctocat.com/assets/images/base-octocat.svg)

View file

@ -1,17 +1,22 @@
#include "TestFramework.h"
#include "CommandLineArgs.h"
#ifdef _WIN32
#include <windows.h>
#endif
//int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
int main()
int main(int argc, char *argv[])
{
#ifdef _WIN32
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
#endif
auto result = TestCaseRunner::getInstance().run();
auto args = CommandLineArgs::Create();
args->process(argc, argv);
auto result = TestCaseRunner::getInstance().run(args->getUserArgs());
#ifdef _WIN32
CoUninitialize();

View file

@ -36,17 +36,30 @@ void TestCaseRunner::markTestFailure(const std::string& line)
sFailureLine = line;
}
bool TestCaseRunner::run()
bool TestCaseRunner::run(const std::vector<std::string>& args)
{
std::string test_to_run;
if (args.size() > 0 )
{
test_to_run = args[0];
}
FileLogger::GetInstance().disable();
for (auto test_case : mCases)
{
if (!test_to_run.empty())
{
if (test_case->getName() != test_to_run)
{
continue;
}
}
sLastTestFailed = false;
std::cout << "TestFramework: Running Test - " << test_case->getName() << std::endl;
test_case->run();
if (sLastTestFailed)
{
std::cout << "Failed at line: " << sLastTestFailed << std::endl;
std::cout << "Failed at line: " << sFailureLine << std::endl;
mFailingTests.push_back(test_case->getName());
}
}

View file

@ -18,7 +18,7 @@ public:
void markTestFailure(const std::string& line);
bool run();
bool run(const std::vector<std::string>& args);
private:
std::vector<std::string> mFailingTests;

View file

@ -16,12 +16,12 @@ struct Holder
static void Test##NAME() \
#define REQUIRE(predicate) \
if(!predicate) \
{ \
TestCaseRunner::getInstance().markTestFailure(std::to_string(__LINE__)); \
return; \
} \
#define REQUIRE(predicate) \
if(!bool(predicate)) \
{ \
TestCaseRunner::getInstance().markTestFailure(std::to_string(__LINE__) + " with check: '" + std::string(#predicate) + "'"); \
return; \
} \

View file

@ -7,9 +7,17 @@ using Path = std::filesystem::path;
class TestUtils
{
public:
static Path getTestOutputDir()
static Path getTestOutputDir(const std::string& testFileName = {})
{
return std::filesystem::current_path() / "test_output";
if (!testFileName.empty())
{
const auto name = Path(testFileName).filename().stem();
return std::filesystem::current_path() / "test_output" / name;
}
else
{
return std::filesystem::current_path() / "test_output";
}
}
static Path getTestDataDir()

View file

@ -11,7 +11,9 @@
#include "TestFramework.h"
#include "TestUtils.h"
TEST_CASE(TestMarkdownParser, "web")
#include <iostream>
TEST_CASE(TestMarkdownParser, "[web]")
{
File md_file(TestUtils::getTestDataDir() / "sample_markdown.md");
const auto md_content = md_file.readText();
@ -19,12 +21,59 @@ TEST_CASE(TestMarkdownParser, "web")
MarkdownParser parser;
auto md_doc = parser.run(md_content);
std::vector<MarkdownElement::Type> expected_top_level = {
MarkdownElement::Type::HEADING,
MarkdownElement::Type::PARAGRAPH,
MarkdownElement::Type::HEADING,
MarkdownElement::Type::PARAGRAPH,
MarkdownElement::Type::MULTILINE_QUOTE,
MarkdownElement::Type::PARAGRAPH,
MarkdownElement::Type::HEADING,
MarkdownElement::Type::PARAGRAPH
};
REQUIRE(expected_top_level.size() <= md_doc->getNumElements());
for(unsigned idx=0; idx<expected_top_level.size(); idx++)
{
REQUIRE(md_doc->getElement(idx)->getType() == expected_top_level[idx]);
}
MarkdownConverter converter;
auto html = converter.convert(md_doc.get());
HtmlWriter writer;
const auto html_string = writer.toString(html.get());
File html_file(TestUtils::getTestOutputDir() / "TestMarkdownParserOut.html");
File html_file(TestUtils::getTestOutputDir(__FILE__) / "TestMarkdownParser.html");
html_file.writeText(html_string);
}
TEST_CASE(TestMarkdownParser_Simple, "[web]")
{
File md_file(TestUtils::getTestDataDir() / "simple_markdown.md");
const auto md_content = md_file.readText();
REQUIRE(!md_content.empty());
MarkdownParser parser;
auto md_doc = parser.run(md_content);
std::vector<MarkdownElement::Type> expected_top_level = {
MarkdownElement::Type::PARAGRAPH,
MarkdownElement::Type::BULLET_LIST};
//REQUIRE(expected_top_level.size() <= md_doc->getNumElements());
for(unsigned idx=0; idx<expected_top_level.size(); idx++)
{
//REQUIRE(md_doc->getElement(idx)->getType() == expected_top_level[idx]);
}
MarkdownConverter converter;
auto html = converter.convert(md_doc.get());
HtmlWriter writer;
const auto html_string = writer.toString(html.get());
File html_file(TestUtils::getTestOutputDir(__FILE__) / "TestMarkdownParser_simple.html");
html_file.writeText(html_string);
}