pcbjam/tests/apps/standalone/xml/xml_test.cpp
Viktor Vaczi a26b40c063 Reorganize project structure for clarity
- Rename tests/wasm-app/ to tests/apps/ for brevity
- Move lib/gl_immediate_shim.js to wasm/shims/ (consolidates WASM files)
- Delete docs/ directory (outdated Nov 2024 research docs)
- Delete patches/ directory (already applied to fork submodules)
- Delete wasm/patches/ and wasm/config/ (empty/unused)
- Update all file references in scripts, configs, and documentation
- Update .gitignore for new tests/apps/ paths

Verified both build workflows pass:
- wxWidgets tests: 255 passed
- Docker KiCad build: completed successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 14:06:23 +01:00

389 lines
12 KiB
C++

// wxXmlDocument Test - XML parsing like KiCad's config and project files
// Tests: wxXmlDocument, wxXmlNode, parsing, creation, traversal
#include "wx/wx.h"
#include "wx/xml/xml.h"
#include "wx/sstream.h"
class XmlFrame : public wxFrame
{
public:
XmlFrame() : wxFrame(nullptr, wxID_ANY, "wxXmlDocument Test",
wxDefaultPosition, wxSize(900, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxXmlDocument for config/project files (665 occurrences).\n"
"Tests: Parsing, node traversal, creation, modification, serialization.");
mainSizer->Add(desc, 0, wxALL, 5);
// Sample XML
wxStaticBoxSizer* sampleSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Sample XML (KiCad-like project)");
m_xmlInput = new wxTextCtrl(mainPanel, wxID_ANY, GetSampleXml(),
wxDefaultPosition, wxSize(-1, 150),
wxTE_MULTILINE | wxTE_DONTWRAP);
m_xmlInput->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
sampleSizer->Add(m_xmlInput, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(sampleSizer, 0, wxEXPAND | wxALL, 5);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* parseBtn = new wxButton(mainPanel, wxID_ANY, "Parse XML");
parseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnParseXml, this);
btnSizer->Add(parseBtn, 0, wxRIGHT, 5);
wxButton* traverseBtn = new wxButton(mainPanel, wxID_ANY, "Traverse Nodes");
traverseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnTraverseNodes, this);
btnSizer->Add(traverseBtn, 0, wxRIGHT, 5);
wxButton* createBtn = new wxButton(mainPanel, wxID_ANY, "Create XML");
createBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnCreateXml, this);
btnSizer->Add(createBtn, 0, wxRIGHT, 5);
wxButton* modifyBtn = new wxButton(mainPanel, wxID_ANY, "Modify XML");
modifyBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnModifyXml, this);
btnSizer->Add(modifyBtn, 0, wxRIGHT, 5);
wxButton* serializeBtn = new wxButton(mainPanel, wxID_ANY, "Serialize");
serializeBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnSerializeXml, this);
btnSizer->Add(serializeBtn, 0);
mainSizer->Add(btnSizer, 0, wxALL, 5);
// Results
wxStaticBoxSizer* resultSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Results / Output");
m_output = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 200),
wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP);
m_output->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
resultSizer->Add(m_output, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(resultSizer, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("XML test app started");
Log("XML test app started");
}
private:
wxString GetSampleXml()
{
return
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kicad_project version=\"1\">\n"
" <general>\n"
" <name>MyProject</name>\n"
" <version>8.0</version>\n"
" </general>\n"
" <schematic>\n"
" <drawing>\n"
" <sheet name=\"Root\" page=\"A4\"/>\n"
" </drawing>\n"
" </schematic>\n"
" <pcb>\n"
" <layers count=\"4\">\n"
" <layer id=\"0\" name=\"F.Cu\" type=\"copper\"/>\n"
" <layer id=\"1\" name=\"In1.Cu\" type=\"copper\"/>\n"
" <layer id=\"2\" name=\"In2.Cu\" type=\"copper\"/>\n"
" <layer id=\"31\" name=\"B.Cu\" type=\"copper\"/>\n"
" </layers>\n"
" <design_rules>\n"
" <track_width min=\"0.2\" default=\"0.25\"/>\n"
" <via_size min=\"0.4\" default=\"0.8\"/>\n"
" </design_rules>\n"
" </pcb>\n"
"</kicad_project>\n";
}
void OnParseXml(wxCommandEvent& event)
{
Log("Parsing XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
Log("Parse failed");
return;
}
wxXmlNode* root = doc.GetRoot();
if (!root)
{
Output("ERROR: No root element");
return;
}
Output(wxString::Format("Parse successful!\n"));
Output(wxString::Format("Root element: <%s>\n", root->GetName()));
Output(wxString::Format("Version attribute: %s\n", root->GetAttribute("version", "none")));
// Count children
int childCount = 0;
wxXmlNode* child = root->GetChildren();
while (child)
{
if (child->GetType() == wxXML_ELEMENT_NODE)
childCount++;
child = child->GetNext();
}
Output(wxString::Format("Child elements: %d\n", childCount));
Log("Parse complete");
}
void OnTraverseNodes(wxCommandEvent& event)
{
Log("Traversing XML nodes...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
return;
}
wxXmlNode* root = doc.GetRoot();
TraverseNode(root, 0);
Log("Traversal complete");
}
void TraverseNode(wxXmlNode* node, int depth)
{
if (!node) return;
wxString indent(depth * 2, ' ');
if (node->GetType() == wxXML_ELEMENT_NODE)
{
wxString attrs;
wxXmlAttribute* attr = node->GetAttributes();
while (attr)
{
attrs += wxString::Format(" %s=\"%s\"", attr->GetName(), attr->GetValue());
attr = attr->GetNext();
}
wxString content = node->GetNodeContent().Trim();
if (!content.IsEmpty())
{
Output(wxString::Format("%s<%s%s>%s</%s>\n",
indent, node->GetName(), attrs, content, node->GetName()));
}
else
{
Output(wxString::Format("%s<%s%s>\n", indent, node->GetName(), attrs));
// Traverse children
wxXmlNode* child = node->GetChildren();
while (child)
{
TraverseNode(child, depth + 1);
child = child->GetNext();
}
if (node->GetChildren())
Output(wxString::Format("%s</%s>\n", indent, node->GetName()));
}
}
}
void OnCreateXml(wxCommandEvent& event)
{
Log("Creating new XML document...");
m_output->Clear();
// Create a new document
wxXmlDocument doc;
doc.SetVersion("1.0");
doc.SetFileEncoding("UTF-8");
// Create root element
wxXmlNode* root = new wxXmlNode(wxXML_ELEMENT_NODE, "component");
root->AddAttribute("type", "resistor");
doc.SetRoot(root);
// Add child elements
wxXmlNode* refNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "reference");
refNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "R1"));
wxXmlNode* valueNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "value");
valueNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "10k"));
wxXmlNode* footprintNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "footprint");
footprintNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "Resistor_SMD:R_0402"));
// Properties
wxXmlNode* propsNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "properties");
wxXmlNode* prop1 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
prop1->AddAttribute("name", "tolerance");
prop1->AddAttribute("value", "1%");
wxXmlNode* prop2 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
prop2->AddAttribute("name", "power");
prop2->AddAttribute("value", "0.1W");
// Serialize to string
wxStringOutputStream outStream;
if (doc.Save(outStream))
{
Output("Created XML document:\n\n");
Output(outStream.GetString());
}
else
{
Output("ERROR: Failed to serialize");
}
Log("XML creation complete");
}
void OnModifyXml(wxCommandEvent& event)
{
Log("Modifying XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
return;
}
wxXmlNode* root = doc.GetRoot();
// Find and modify the general/name element
wxXmlNode* general = FindChild(root, "general");
if (general)
{
wxXmlNode* name = FindChild(general, "name");
if (name && name->GetChildren())
{
wxString oldName = name->GetNodeContent();
name->GetChildren()->SetContent("ModifiedProject");
Output(wxString::Format("Changed project name: '%s' -> 'ModifiedProject'\n", oldName));
}
}
// Add a new element to pcb
wxXmlNode* pcb = FindChild(root, "pcb");
if (pcb)
{
wxXmlNode* newElem = new wxXmlNode(wxXML_ELEMENT_NODE, "modified");
newElem->AddAttribute("timestamp", wxDateTime::Now().FormatISOCombined());
pcb->AddChild(newElem);
Output("Added <modified> element to <pcb>\n");
}
// Serialize modified document
wxStringOutputStream outStream;
if (doc.Save(outStream))
{
Output("\nModified XML:\n");
Output(outStream.GetString());
}
Log("Modification complete");
}
void OnSerializeXml(wxCommandEvent& event)
{
Log("Serializing XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream inStream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(inStream))
{
Output("ERROR: Failed to parse XML");
return;
}
// Serialize with formatting
wxStringOutputStream outStream;
if (doc.Save(outStream, 2)) // Indent with 2 spaces
{
Output("Serialized XML (formatted):\n\n");
Output(outStream.GetString());
// Show statistics
wxString serialized = outStream.GetString();
Output(wxString::Format("\n--- Statistics ---\n"));
Output(wxString::Format("Total size: %zu bytes\n", serialized.Length()));
Output(wxString::Format("Lines: %d\n", serialized.Freq('\n') + 1));
}
Log("Serialization complete");
}
wxXmlNode* FindChild(wxXmlNode* parent, const wxString& name)
{
if (!parent) return nullptr;
wxXmlNode* child = parent->GetChildren();
while (child)
{
if (child->GetType() == wxXML_ELEMENT_NODE && child->GetName() == name)
return child;
child = child->GetNext();
}
return nullptr;
}
void Output(const wxString& text)
{
m_output->AppendText(text);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxTextCtrl* m_xmlInput;
wxTextCtrl* m_output;
wxTextCtrl* m_log;
};
class XmlApp : public wxApp
{
public:
virtual bool OnInit() override
{
XmlFrame* frame = new XmlFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(XmlApp);