66#include < QCoreApplication>
77#include < QEventLoop>
88#include < QTimer>
9+ #include < QProcess>
10+ #include < QTemporaryFile>
11+ #include < QTextStream>
912#include < cassert>
1013#include < iostream>
1114
1215// Simple test for NgspiceShared class
1316// Tests that we can initialize ngspice, load a circuit, run simulation, and get output
1417
18+ // Helper class for running ngspice as external process
19+ class NgspiceExternalProcess {
20+ public:
21+ QString runNetlist (const QStringList &netlist, int timeout_ms = 5000 ) {
22+ // Create temporary netlist file
23+ QTemporaryFile netlistFile;
24+ netlistFile.setAutoRemove (false );
25+ if (!netlistFile.open ()) {
26+ std::cerr << " Failed to create temporary netlist file" << std::endl;
27+ return QString ();
28+ }
29+
30+ QTextStream stream (&netlistFile);
31+ for (const QString &line : netlist) {
32+ stream << line << " \n " ;
33+ }
34+ netlistFile.close ();
35+
36+ // Run ngspice
37+ QProcess process;
38+ process.start (" ngspice" , QStringList () << " -b" << netlistFile.fileName ());
39+
40+ if (!process.waitForStarted (timeout_ms)) {
41+ std::cerr << " Failed to start ngspice process" << std::endl;
42+ netlistFile.remove ();
43+ return QString ();
44+ }
45+
46+ if (!process.waitForFinished (timeout_ms)) {
47+ std::cerr << " Ngspice process timed out" << std::endl;
48+ process.kill ();
49+ netlistFile.remove ();
50+ return QString ();
51+ }
52+
53+ QString output = QString::fromLocal8Bit (process.readAllStandardOutput ());
54+ QString errors = QString::fromLocal8Bit (process.readAllStandardError ());
55+
56+ netlistFile.remove ();
57+
58+ return output + errors;
59+ }
60+ };
61+
62+ // Global ngspice instance to avoid multiple initializations
63+ static NgspiceShared* g_ngspice = nullptr ;
64+
1565void test_basic_initialization () {
1666 std::cout << " Test: Basic initialization..." << std::endl;
1767
18- NgspiceShared ngspice ;
19- bool initialized = ngspice. initialize ();
68+ g_ngspice = new NgspiceShared () ;
69+ bool initialized = g_ngspice-> initialize ();
2070
2171 assert (initialized && " NgspiceShared should initialize successfully" );
2272 std::cout << " ✓ Initialization successful" << std::endl;
@@ -25,8 +75,8 @@ void test_basic_initialization() {
2575void test_simple_circuit () {
2676 std::cout << " \n Test: Simple voltage divider circuit..." << std::endl;
2777
28- NgspiceShared ngspice ;
29- assert (ngspice. initialize () && " Initialization failed " ) ;
78+ assert (g_ngspice != nullptr && " NgspiceShared not initialized " ) ;
79+ NgspiceShared& ngspice = *g_ngspice ;
3080
3181 // Simple voltage divider circuit
3282 // V1 connected to R1, R1 connected to R2, R2 connected to ground
@@ -107,8 +157,8 @@ void test_simple_circuit() {
107157void test_error_handling () {
108158 std::cout << " \n Test: Error handling..." << std::endl;
109159
110- NgspiceShared ngspice ;
111- assert (ngspice. initialize () && " Initialization failed " ) ;
160+ assert (g_ngspice != nullptr && " NgspiceShared not initialized " ) ;
161+ NgspiceShared& ngspice = *g_ngspice ;
112162
113163 // Try to send an invalid command
114164 int result = ngspice.sendCommand (" invalid_command_xyz" );
@@ -125,26 +175,113 @@ void test_error_handling() {
125175 std::cout << " ✓ Bad netlist handled (result: " << result << " )" << std::endl;
126176}
127177
178+ void test_shared_vs_external () {
179+ std::cout << " \n Test: Comparing shared library vs external process..." << std::endl;
180+
181+ // Create a simple test circuit
182+ QStringList netlist;
183+ netlist << " Voltage Divider Comparison Test" ;
184+ netlist << " V1 n1 0 DC 12" ;
185+ netlist << " R1 n1 n2 2k" ;
186+ netlist << " R2 n2 0 2k" ;
187+ netlist << " .op" ;
188+ netlist << " .print dc v(n2)" ;
189+ netlist << " .end" ;
190+
191+ // Test with shared library
192+ std::cout << " Running with shared library..." << std::endl;
193+ assert (g_ngspice != nullptr && " NgspiceShared not initialized" );
194+
195+ QString sharedOutput;
196+ QObject::connect (g_ngspice, &NgspiceShared::outputReceived,
197+ [&sharedOutput](const QString& text) {
198+ sharedOutput += text;
199+ });
200+
201+ int result = g_ngspice->sendCircuit (netlist);
202+ assert (result == 0 && " Shared library circuit loading failed" );
203+
204+ result = g_ngspice->sendCommand (" run" );
205+ assert (result == 0 && " Shared library run command failed" );
206+
207+ // Give simulation time to complete
208+ QEventLoop loop;
209+ QTimer::singleShot (1000 , &loop, &QEventLoop::quit);
210+ loop.exec ();
211+
212+ // Get vectors from shared library
213+ QString sharedPlot = g_ngspice->currentPlot ();
214+ QStringList sharedVectors = g_ngspice->allVectors (sharedPlot);
215+
216+ std::cout << " Shared library plot: " << sharedPlot.toStdString () << std::endl;
217+ std::cout << " Shared library vectors: " << sharedVectors.size () << std::endl;
218+
219+ // Test with external process
220+ std::cout << " Running with external process..." << std::endl;
221+ NgspiceExternalProcess ngspiceExternal;
222+ QString externalOutput = ngspiceExternal.runNetlist (netlist);
223+
224+ assert (!externalOutput.isEmpty () && " External process produced no output" );
225+ std::cout << " External process completed" << std::endl;
226+ std::cout << " External output length: " << externalOutput.length () << " chars" << std::endl;
227+
228+ // Check if output contains expected indicators
229+ bool hasCircuitName = externalOutput.contains (" Voltage Divider Comparison" );
230+ bool hasAnalysis = externalOutput.contains (" Doing analysis" ) || externalOutput.contains (" Circuit:" );
231+ bool hasNode = externalOutput.contains (" n2" ) || externalOutput.contains (" v-sweep" );
232+
233+ std::cout << " Has circuit name: " << (hasCircuitName ? " yes" : " no" ) << std::endl;
234+ std::cout << " Has analysis: " << (hasAnalysis ? " yes" : " no" ) << std::endl;
235+ std::cout << " Has node reference: " << (hasNode ? " yes" : " no" ) << std::endl;
236+
237+ // Both should complete successfully
238+ assert (sharedVectors.size () > 0 && " Shared library: No output vectors" );
239+ assert (hasAnalysis && " External process: No analysis output" );
240+
241+ // Both should have the voltage node
242+ bool sharedHasN2 = false ;
243+ for (const QString& vec : sharedVectors) {
244+ if (vec.contains (" n2" )) {
245+ sharedHasN2 = true ;
246+ break ;
247+ }
248+ }
249+
250+ assert (sharedHasN2 && " Shared library: Missing n2 voltage node" );
251+
252+ std::cout << " ✓ Both modes completed successfully" << std::endl;
253+ std::cout << " ✓ Both modes produced expected outputs" << std::endl;
254+ }
255+
128256int main (int argc, char ** argv) {
129257 QCoreApplication app (argc, argv);
130258
131259 std::cout << " === NgspiceShared Test Suite ===" << std::endl;
132260 std::cout << std::endl;
133261
262+ int exitCode = 0 ;
134263 try {
135264 test_basic_initialization ();
136265 test_simple_circuit ();
137266 test_error_handling ();
267+ test_shared_vs_external ();
138268
139269 std::cout << " \n === All tests passed! ===" << std::endl;
140- return 0 ;
141270 } catch (const std::exception& e) {
142271 std::cerr << " \n !!! Test failed with exception: " << e.what () << std::endl;
143- return 1 ;
272+ exitCode = 1 ;
144273 } catch (...) {
145274 std::cerr << " \n !!! Test failed with unknown exception" << std::endl;
146- return 1 ;
275+ exitCode = 1 ;
276+ }
277+
278+ // Cleanup: avoid explicit delete to prevent ngspice cleanup issues
279+ if (g_ngspice) {
280+ g_ngspice->setParent (nullptr );
281+ g_ngspice = nullptr ;
147282 }
283+
284+ return exitCode;
148285}
149286
150287#else
0 commit comments