1 /*
2 * Copyright (C) 2007-2010 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include "assert.h"
29 #include <iostream>
30
31 #include "GXemul.h"
32 #include "CommandInterpreter.h"
33
34 // Built-in commands (autogenerated list by the configure script):
35 #include "../../commands_h.h"
36
37
CommandInterpreter(GXemul * owner)38 CommandInterpreter::CommandInterpreter(GXemul* owner)
39 : m_GXemul(owner)
40 , m_currentCommandCursorPosition(0)
41 , m_inEscapeSequence(false)
42 , m_historyEntryToCopyFrom(0)
43 , m_commandHistoryInsertPosition(0)
44 , m_commandHistoryMaxSize(100)
45 {
46 m_commandHistory.resize(m_commandHistoryMaxSize, "");
47
48 // It would be bad to run without a working GXemul instance.
49 assert(m_GXemul != NULL);
50
51 // Add the default built-in commands:
52 // (This list is autogenerated by the configure script.)
53 #include "../../commands.h"
54 }
55
56
AddCommand(refcount_ptr<Command> command)57 void CommandInterpreter::AddCommand(refcount_ptr<Command> command)
58 {
59 m_commands[command->GetCommandName()] = command;
60 }
61
62
GetCommands() const63 const Commands& CommandInterpreter::GetCommands() const
64 {
65 return m_commands;
66 }
67
68
AddLineToCommandHistory(const string & command)69 int CommandInterpreter::AddLineToCommandHistory(const string& command)
70 {
71 if (command == "")
72 return m_commandHistoryInsertPosition;
73
74 size_t lastInsertedPosition =
75 (m_commandHistoryInsertPosition - 1 + m_commandHistoryMaxSize)
76 % m_commandHistoryMaxSize;
77
78 if (m_commandHistory[lastInsertedPosition] == command)
79 return m_commandHistoryInsertPosition;
80
81 m_commandHistory[m_commandHistoryInsertPosition ++] = command;
82 m_commandHistoryInsertPosition %= m_commandHistoryMaxSize;
83
84 return m_commandHistoryInsertPosition;
85 }
86
87
GetHistoryLine(int nStepsBack) const88 string CommandInterpreter::GetHistoryLine(int nStepsBack) const
89 {
90 if (nStepsBack == 0)
91 return "";
92
93 int index = (m_commandHistoryInsertPosition - nStepsBack +
94 m_commandHistoryMaxSize) % m_commandHistoryMaxSize;
95
96 return m_commandHistory[index];
97 }
98
99
TabComplete(string & commandString,size_t & cursorPosition,bool visibleShowAvailable)100 bool CommandInterpreter::TabComplete(string& commandString,
101 size_t& cursorPosition, bool visibleShowAvailable)
102 {
103 string wordToComplete;
104 bool firstWordOnLine = true;
105
106 size_t pos = cursorPosition;
107 while (pos > 0) {
108 pos --;
109 if (commandString[pos] == ' ')
110 break;
111 wordToComplete = commandString[pos] + wordToComplete;
112 }
113
114 while (pos > 0) {
115 pos --;
116 if (commandString[pos] != ' ') {
117 firstWordOnLine = false;
118 break;
119 }
120 }
121
122 bool completeCommands = firstWordOnLine;
123
124 if (wordToComplete == "") {
125 if (!visibleShowAvailable)
126 return false;
127
128 // Show all available words:
129
130 if (completeCommands) {
131 // All available commands:
132 vector<string> allCommands;
133 for (Commands::const_iterator it = m_commands.begin();
134 it != m_commands.end(); ++it)
135 allCommands.push_back(it->first);
136
137 ShowAvailableWords(allCommands);
138 }
139
140 ShowAvailableWords(m_GXemul->GetRootComponent()->
141 FindPathByPartialMatch(""));
142
143 return false;
144 }
145
146 vector<string> matches;
147
148 matches = m_GXemul->GetRootComponent()->
149 FindPathByPartialMatch(wordToComplete, true);
150
151 if (completeCommands) {
152 Commands::const_iterator it = m_commands.begin();
153 for (; it != m_commands.end(); ++it) {
154 const string& commandName = it->first;
155 if (commandName.substr(0, wordToComplete.length())
156 == wordToComplete) {
157 matches.push_back(commandName);
158 }
159 }
160 }
161
162 if (matches.size() == 0)
163 return false;
164
165 string completedWord;
166
167 // Single match, or multiple matches?
168 if (matches.size() == 1) {
169 // Insert the rest of the command name into the input line:
170 completedWord = matches[0];
171 } else {
172 // Figure out the longest possible match, and add that:
173 size_t i, n = matches.size();
174 for (size_t pos2 = 0; ; pos2 ++) {
175 if (pos2 >= matches[0].length())
176 break;
177 stringchar ch = matches[0][pos2];
178 for (i=1; i<n; i++) {
179 if (matches[i][pos2] != ch)
180 break;
181 }
182 if (i == n)
183 completedWord += ch;
184 else
185 break;
186 }
187
188 // Show available words, so the user knows what there
189 // is to choose from.
190 if (visibleShowAvailable)
191 ShowAvailableWords(matches);
192 }
193
194 // Erase the old (incomplete) word, and insert the completed word:
195 if (!completedWord.empty()) {
196 cursorPosition -= wordToComplete.length();
197 commandString.erase(cursorPosition, wordToComplete.length());
198 commandString.insert(cursorPosition, completedWord);
199 cursorPosition += completedWord.length();
200 }
201
202 // Special case: If there was a single match, and we are at the end
203 // of the line, and this was a command, then add a space (" "). This
204 // behaviour feels better, and this is how other tab completors seems
205 // to work.
206 //
207 // NOTE: Don't add a space after component paths. Usually the user
208 // will want to type e.g. "cpu" + TAB, and get
209 // "root.machine0.mainbus0.cpu0" with no space, and then be able to
210 // add ".unassemble" or so manually.
211 if (matches.size() == 1 && cursorPosition == commandString.length()) {
212 bool isCommand = false;
213 Commands::const_iterator it = m_commands.begin();
214 for (; it != m_commands.end(); ++it) {
215 const string& commandName = it->first;
216 if (commandName == matches[0]) {
217 isCommand = true;
218 break;
219 }
220 }
221
222 if (isCommand) {
223 commandString += " ";
224 cursorPosition ++;
225 }
226 }
227
228 return matches.size() == 1;
229 }
230
231
TabCompleteWithSubname(string & commandString,size_t & cursorPosition,bool visibleShowAvailable)232 bool CommandInterpreter::TabCompleteWithSubname(string& commandString,
233 size_t& cursorPosition, bool visibleShowAvailable)
234 {
235 if (cursorPosition == 0)
236 return false;
237
238 int nStepsBack = 1;
239 size_t pos = cursorPosition - 1;
240
241 while (pos > 0 && commandString[pos] != '.') {
242 pos --;
243 nStepsBack ++;
244 }
245
246 if (pos == 0)
247 return false;
248
249 // Here, pos is the position of the dot:
250 //
251 // cpu.u
252 // ^
253
254 bool success = TabComplete(commandString, pos, visibleShowAvailable);
255 if (!success)
256 return false;
257
258 // pos is now the new position of the dot:
259 //
260 // root.machine0.mainbus0.cpu.u
261 // ^
262
263 // Look up the component:
264 int startOfComponentName = pos;
265 while (startOfComponentName >= 0 &&
266 commandString[startOfComponentName] != ' ')
267 -- startOfComponentName;
268
269 if (startOfComponentName < 0)
270 startOfComponentName = 0;
271
272 string componentName = commandString.substr(startOfComponentName,
273 pos - startOfComponentName);
274
275 // std::cerr << "[" << componentName << "]\n";
276 refcount_ptr<Component> component = m_GXemul->GetRootComponent()->
277 LookupPath(componentName);
278
279 cursorPosition = pos + nStepsBack;
280
281 if (component.IsNULL())
282 return false;
283
284 // Figure out the method or state name to expand:
285 size_t startOfMethodName = pos + 1;
286 size_t methodNameLen = 0;
287 while (startOfMethodName + methodNameLen < cursorPosition &&
288 commandString[startOfMethodName+methodNameLen] != ' ')
289 methodNameLen ++;
290
291 string methodName = commandString.substr(startOfMethodName,
292 methodNameLen);
293
294 // std::cerr << "{" << methodName << "}\n";
295
296 vector<string> names;
297 vector<string> matchingNames;
298 component->GetMethodNames(names);
299 component->GetVariableNames(names);
300 int nrOfMatches = 0;
301 if (methodName.length() != 0) {
302 for (size_t i=0; i<names.size(); ++i) {
303 if (names[i].substr(0, methodName.length()) ==
304 methodName) {
305 ++ nrOfMatches;
306 matchingNames.push_back(names[i]);
307 }
308 }
309 } else {
310 matchingNames = names;
311 }
312
313 if (matchingNames.size() == 0)
314 return false;
315
316 // Replace the short name with a match as long as possible, e.g.
317 // "memo" will be replaced by "memoryMapped", if names
318 // "memoryMappedAddr" and "memoryMappedSize" are available.
319 string longestPossibleMatch = "";
320 size_t i, n = matchingNames.size();
321 for (size_t pos2 = 0; ; pos2 ++) {
322 if (pos2 >= matchingNames[0].length())
323 break;
324 stringchar ch = matchingNames[0][pos2];
325 for (i=1; i<n; i++) {
326 if (matchingNames[i][pos2] != ch)
327 break;
328 }
329 if (i == n)
330 longestPossibleMatch += ch;
331 else
332 break;
333 }
334
335 commandString.replace(startOfMethodName, methodNameLen,
336 longestPossibleMatch);
337 cursorPosition += longestPossibleMatch.length() - methodNameLen;
338
339 // A single match? Then we succeeded.
340 if (nrOfMatches == 1)
341 return true;
342
343 // Show available methods and variable names:
344 if (visibleShowAvailable) {
345 vector<string> allNames;
346 vector<string> matchingNames2;
347
348 component->GetMethodNames(allNames);
349 for (size_t j=0; j<allNames.size(); ++j) {
350 if (methodName.length() == 0 ||
351 allNames[j].substr(0, methodName.length()) == methodName)
352 matchingNames2.push_back(allNames[j]);
353 }
354
355 if (matchingNames2.size() > 0) {
356 m_GXemul->GetUI()->ShowDebugMessage("\nMethods:");
357 ShowAvailableWords(matchingNames2);
358 }
359 }
360 if (visibleShowAvailable) {
361 vector<string> allNames;
362 vector<string> matchingNames2;
363
364 component->GetVariableNames(allNames);
365 for (size_t j=0; j<allNames.size(); ++j) {
366 if (methodName.length() == 0 ||
367 allNames[j].substr(0, methodName.length()) == methodName)
368 matchingNames2.push_back(allNames[j]);
369 }
370
371 if (matchingNames2.size() > 0) {
372 m_GXemul->GetUI()->ShowDebugMessage("\nVariables:");
373 ShowAvailableWords(matchingNames2);
374 }
375 }
376
377 return false;
378 }
379
380
AddKey(stringchar key)381 bool CommandInterpreter::AddKey(stringchar key)
382 {
383 if (m_inEscapeSequence) {
384 m_escapeSequence += key;
385
386 // Handle some common escape sequences, and convert
387 // them into simpler 1-byte keys/characters:
388
389 if (m_escapeSequence == "[C") { // right
390 m_inEscapeSequence = false;
391 AddKey('\6'); // CTRL-F
392 } else if (m_escapeSequence == "[D") { // left
393 m_inEscapeSequence = false;
394 AddKey('\2'); // CTRL-B
395 } else if (m_escapeSequence == "OH") { // home
396 m_inEscapeSequence = false;
397 AddKey('\1'); // CTRL-A
398 } else if (m_escapeSequence == "[H") { // home
399 m_inEscapeSequence = false;
400 AddKey('\1'); // CTRL-A
401 } else if (m_escapeSequence == "OF") { // end
402 m_inEscapeSequence = false;
403 AddKey('\5'); // CTRL-E
404 } else if (m_escapeSequence == "[F") { // end
405 m_inEscapeSequence = false;
406 AddKey('\5'); // CTRL-E
407 } else if (m_escapeSequence == "[A") { // up
408 m_inEscapeSequence = false;
409 AddKey('\20'); // CTRL-P
410 } else if (m_escapeSequence == "[B") { // down
411 m_inEscapeSequence = false;
412 AddKey('\16'); // CTRL-N
413 } else if (m_escapeSequence.length() > 2) {
414 // Let's bail out of escape sequence handling...
415 //
416 // Note: If you trace execution here for some key that
417 // you feel _should_ be handled, please send me a mail
418 // about it.
419 //
420 m_inEscapeSequence = false;
421 AddKey('?');
422 }
423
424 return false;
425 }
426
427 switch (key) {
428
429 case '\0':
430 // Add nothing, just reshow/update the command buffer.
431 break;
432
433 case '\1': // CTRL-A: move to start of line
434 m_currentCommandCursorPosition = 0;
435 break;
436
437 case '\2': // CTRL-B: move back (left)
438 if (m_currentCommandCursorPosition > 0)
439 m_currentCommandCursorPosition --;
440 break;
441
442 case '\4': // CTRL-D: remove the character to the right
443 if (m_currentCommandCursorPosition <
444 m_currentCommandString.length())
445 m_currentCommandString.erase(
446 m_currentCommandCursorPosition, 1);
447 break;
448
449 case '\5': // CTRL-E: move to end of line
450 m_currentCommandCursorPosition =
451 m_currentCommandString.length();
452 break;
453
454 case '\6': // CTRL-F: move forward (right)
455 if (m_currentCommandCursorPosition <
456 m_currentCommandString.length())
457 m_currentCommandCursorPosition ++;
458 break;
459
460 case '\13': // CTRL-K: kill to end of line
461 ClearCurrentInputLineVisually();
462 m_currentCommandString.resize(m_currentCommandCursorPosition);
463 break;
464
465 case '\16': // CTRL-N: next in history (down)
466 ClearCurrentInputLineVisually();
467
468 m_historyEntryToCopyFrom --;
469 if (m_historyEntryToCopyFrom < 0)
470 m_historyEntryToCopyFrom = 0;
471
472 m_currentCommandString =
473 GetHistoryLine(m_historyEntryToCopyFrom);
474 m_currentCommandCursorPosition =
475 m_currentCommandString.length();
476 break;
477
478 case '\20': // CTRL-P: previous in history (up)
479 ClearCurrentInputLineVisually();
480
481 m_historyEntryToCopyFrom ++;
482 m_currentCommandString =
483 GetHistoryLine(m_historyEntryToCopyFrom);
484
485 // We went too far? Then back down.
486 if (m_currentCommandString == "") {
487 m_historyEntryToCopyFrom --;
488 m_currentCommandString =
489 GetHistoryLine(m_historyEntryToCopyFrom);
490 }
491 m_currentCommandCursorPosition =
492 m_currentCommandString.length();
493 break;
494
495 case '\24': // CTRL-T: show status
496 m_GXemul->GetUI()->ShowDebugMessage("\n");
497 RunCommand("status");
498 break;
499
500 case '\27': // CTRL-W: remove current word (backspacing)
501 ClearCurrentInputLineVisually();
502
503 // 1. Remove any spaces left to the cursor.
504 while (m_currentCommandCursorPosition > 0) {
505 if (m_currentCommandString[
506 m_currentCommandCursorPosition-1] == ' ') {
507 m_currentCommandCursorPosition --;
508 m_currentCommandString.erase(
509 m_currentCommandCursorPosition, 1);
510 } else {
511 break;
512 }
513 }
514
515 // 2. Remove non-spaces left to the cusror, either until
516 // the cursor is at position 0, or until there is a
517 // space again.
518 while (m_currentCommandCursorPosition > 0) {
519 if (m_currentCommandString[
520 m_currentCommandCursorPosition-1] != ' ') {
521 m_currentCommandCursorPosition --;
522 m_currentCommandString.erase(
523 m_currentCommandCursorPosition, 1);
524 } else {
525 break;
526 }
527 }
528
529 break;
530
531 case '\177': // ASCII 127 (octal 177) = del
532 case '\b': // backspace
533 if (m_currentCommandCursorPosition > 0) {
534 m_currentCommandCursorPosition --;
535 m_currentCommandString.erase(
536 m_currentCommandCursorPosition, 1);
537 }
538 break;
539
540 case '\33':
541 // Escape key handling:
542 m_inEscapeSequence = true;
543 m_escapeSequence = "";
544 break;
545
546 case '\t':
547 // Tab completion, with visible word hints:
548 {
549 bool success = TabComplete(m_currentCommandString,
550 m_currentCommandCursorPosition, true);
551
552 // Attempt to expand component-name + "." + optional
553 // method or variable name, "cpu.u", if the first
554 // tab-completion failed.
555 if (!success) {
556 TabCompleteWithSubname(m_currentCommandString,
557 m_currentCommandCursorPosition, true);
558 }
559 }
560 break;
561
562 case '\n':
563 case '\r':
564 // Newline executes the command, if it is non-empty:
565 m_GXemul->GetUI()->InputLineDone();
566
567 if (!m_currentCommandString.empty()) {
568 AddLineToCommandHistory(m_currentCommandString);
569 bool ignoredResult;
570 RunCommand(m_currentCommandString, &ignoredResult);
571 ClearCurrentCommandBuffer();
572 } else if (m_mayBeReexecuted != "") {
573 RunCommand(m_mayBeReexecuted);
574 }
575 break;
576
577 default:
578 // Most other keys just add/insert a character into the command
579 // string:
580 if (key >= ' ') {
581 m_currentCommandString.insert(
582 m_currentCommandCursorPosition, 1, key);
583 m_currentCommandCursorPosition ++;
584 }
585 }
586
587 if (key != '\n' && key != '\r')
588 ReshowCurrentCommandBuffer();
589
590 // Return value is true for newline/cr, false otherwise:
591 return key == '\n' || key == '\r';
592 }
593
594
ShowAvailableWords(const vector<string> & words)595 void CommandInterpreter::ShowAvailableWords(const vector<string>& words)
596 {
597 m_GXemul->GetUI()->ShowDebugMessage("\n");
598
599 const size_t n = words.size();
600 size_t i;
601
602 // Find the longest word first:
603 size_t maxLen = 0;
604 for (i=0; i<n; ++i) {
605 size_t len = words[i].length();
606 if (len > maxLen)
607 maxLen = len;
608 }
609
610 maxLen += 4;
611
612 // Generate msg:
613 std::stringstream msg;
614 size_t lineLen = 0;
615 for (i=0; i<n; ++i) {
616 if (lineLen == 0)
617 msg << " ";
618
619 size_t len = words[i].length();
620 msg << words[i];
621 lineLen += len;
622
623 for (size_t j=len; j<maxLen; j++) {
624 msg << " ";
625 lineLen ++;
626 }
627
628 if (lineLen >= 77 - maxLen || i == n-1) {
629 msg << "\n";
630 lineLen = 0;
631 }
632 }
633
634 m_GXemul->GetUI()->ShowDebugMessage(msg.str());
635 }
636
637
ReshowCurrentCommandBuffer()638 void CommandInterpreter::ReshowCurrentCommandBuffer()
639 {
640 m_GXemul->GetUI()->RedisplayInputLine(
641 m_currentCommandString, m_currentCommandCursorPosition);
642 }
643
644
ClearCurrentInputLineVisually()645 void CommandInterpreter::ClearCurrentInputLineVisually()
646 {
647 string clearString = "";
648 clearString.insert((size_t)0, m_currentCommandString.length(), ' ');
649
650 m_GXemul->GetUI()->RedisplayInputLine(
651 clearString, m_currentCommandCursorPosition);
652 }
653
654
ClearCurrentCommandBuffer()655 void CommandInterpreter::ClearCurrentCommandBuffer()
656 {
657 m_currentCommandString = "";
658 m_currentCommandCursorPosition = 0;
659 m_historyEntryToCopyFrom = 0;
660 }
661
662
SplitIntoWords(const string & commandOrig,string & commandName,vector<string> & arguments)663 static void SplitIntoWords(const string& commandOrig,
664 string& commandName, vector<string>& arguments)
665 {
666 string command = commandOrig;
667
668 arguments.clear();
669 commandName = "";
670 size_t pos = 0;
671
672 // Surround '=' with white spaces, except when inside parentheses...
673 // NOTE/TODO: This will not be needed in the future (?), when a real
674 // expression evaluator has been [re]implemented.
675 int insideParenthesesCount = 0;
676 while (pos < command.length()) {
677 if (command[pos] == '(')
678 insideParenthesesCount ++;
679 if (command[pos] == ')')
680 insideParenthesesCount --;
681 if (command[pos] == '=' && insideParenthesesCount == 0) {
682 command.replace(pos, 1, " = ");
683 pos ++;
684 }
685
686 pos ++;
687 }
688
689 // Split command into words, ignoring all whitespace:
690 pos = 0;
691 while (pos < command.length()) {
692 // Skip initial whitespace:
693 while (pos < command.length() && command[pos] == ' ')
694 pos ++;
695
696 if (pos >= command.length())
697 break;
698
699 // This is a new word. Add all characters, until
700 // whitespace or end of string:
701 string newWord = "";
702 while (pos < command.length() && command[pos] != ' ') {
703 newWord += command[pos];
704 pos ++;
705 }
706
707 if (commandName.empty())
708 commandName = newWord;
709 else
710 arguments.push_back(newWord);
711 }
712 }
713
714
VariableAssignment(const string & componentPath,const string & variableName,const string & expression)715 void CommandInterpreter::VariableAssignment(const string& componentPath,
716 const string& variableName, const string& expression)
717 {
718 refcount_ptr<Component> component = m_GXemul->GetRootComponent()->
719 LookupPath(componentPath);
720
721 StateVariable* var = component->GetVariable(variableName);
722 if (var == NULL) {
723 m_GXemul->GetUI()->ShowDebugMessage("Unknown variable '" +
724 variableName + "'? (Internal error.)\n");
725 throw std::exception();
726 }
727
728 const refcount_ptr<Component> lightClone =
729 m_GXemul->GetRootComponent()->LightClone();
730
731 // Attempt to assign the expression to the variable:
732 if (!component->SetVariableValue(variableName, expression))
733 m_GXemul->GetUI()->ShowDebugMessage("Assignment failed.\n");
734
735 // ... and print all state change (in case a write to a variable had
736 // side effects, then this makes sure that the user sees all such side
737 // effects):
738 stringstream changeMessages;
739 m_GXemul->GetRootComponent()->DetectChanges(lightClone, changeMessages);
740
741 string msg = changeMessages.str();
742 if (msg == "")
743 msg = "(No state change.)\n";
744
745 m_GXemul->GetUI()->ShowDebugMessage(msg);
746 }
747
748
RunComponentMethod(const string & componentPathAndMethod,const vector<string> & arguments)749 bool CommandInterpreter::RunComponentMethod(
750 const string& componentPathAndMethod, const vector<string>& arguments)
751 {
752 // Note: componentPathAndMethod may or may not have a method at
753 // the end!
754
755 // Make several "smart" guesses:
756 refcount_ptr<Component> component;
757 string componentPath;
758 string methodName;
759
760 do {
761 // 1. Assume that componentPathAndMethod is a full component
762 // path:
763 component = m_GXemul->GetRootComponent()->
764 LookupPath(componentPathAndMethod);
765 if (!component.IsNULL())
766 break;
767
768 // 2. Assume that componentPathAndMethod is a component
769 // path, but it is not tab-completed yet:
770 string tabcompleted = componentPathAndMethod;
771 size_t tmpLen = tabcompleted.length();
772 if (TabComplete(tabcompleted, tmpLen)) {
773 component = m_GXemul->GetRootComponent()->
774 LookupPath(tabcompleted);
775 if (!component.IsNULL())
776 break;
777 }
778
779 // If there is no period in the name, we can't continue
780 // with the following guesses.
781 if (componentPathAndMethod.find(".") == string::npos)
782 break;
783
784 size_t pos = componentPathAndMethod.find_last_of('.');
785
786 // 3. Assume full component path + ".method":
787 componentPath = componentPathAndMethod.substr(0, pos);
788 component = m_GXemul->GetRootComponent()->
789 LookupPath(componentPath);
790 if (!component.IsNULL()) {
791 methodName = componentPathAndMethod.substr(pos+1);
792 break;
793 }
794
795 // 4. Assume non-tab-completed component path + ".method":
796 tabcompleted = componentPath;
797 tmpLen = tabcompleted.length();
798 if (TabComplete(tabcompleted, tmpLen)) {
799 component = m_GXemul->GetRootComponent()->
800 LookupPath(tabcompleted);
801 if (!component.IsNULL()) {
802 methodName =
803 componentPathAndMethod.substr(pos+1);
804 break;
805 }
806 }
807 } while (false);
808
809 if (component.IsNULL())
810 return false;
811
812 // No method given? Then show the component tree, and the component's
813 // state variables, and return.
814 if (methodName.empty()) {
815 m_GXemul->GetUI()->ShowDebugMessage(
816 component->GenerateTreeDump(""));
817
818 // Retrieve the names of all the state variables:
819 vector<string> variableNames;
820 component->GetVariableNames(variableNames);
821
822 stringstream ss;
823 ss << "\n";
824
825 size_t maxLen = 0;
826 size_t i;
827 for (i=0; i<variableNames.size(); i++)
828 if (variableNames[i].length() > maxLen)
829 maxLen = variableNames[i].length();
830
831 for (i=0; i<variableNames.size(); i++) {
832 const string& name = variableNames[i];
833 if (name == "name" || name == "template")
834 continue;
835
836 ss << " " << name;
837 for (size_t j=name.length(); j<=maxLen; j++)
838 ss << " ";
839
840 const StateVariable* var = component->GetVariable(name);
841 if (var == NULL)
842 ss << "= (unknown?)";
843 else
844 ss << "= " << var->ToString();
845
846 ss << "\n";
847 }
848
849 m_GXemul->GetUI()->ShowDebugMessage(ss.str());
850
851 return true;
852 }
853
854 // Now, it is possible that methodName is incomplete, so it has to
855 // be looked up as well:
856 vector<string> names;
857 component->GetMethodNames(names);
858 int nrOfMatches = 0;
859 string fullMatch;
860 for (size_t i=0; i<names.size(); ++i) {
861 if (names[i].substr(0, methodName.length()) == methodName) {
862 ++ nrOfMatches;
863 fullMatch = names[i];
864 }
865 }
866
867 if (nrOfMatches == 1) {
868 // Execute it!
869 component->ExecuteMethod(m_GXemul, fullMatch, arguments);
870
871 if (component->MethodMayBeReexecutedWithoutArgs(fullMatch))
872 m_mayBeReexecuted = componentPathAndMethod;
873
874 return true;
875 }
876
877 // Try variable names:
878 names.clear();
879 component->GetVariableNames(names);
880 nrOfMatches = 0;
881 fullMatch = "";
882 for (size_t i=0; i<names.size(); ++i) {
883 // Exact match?
884 if (names[i] == methodName) {
885 nrOfMatches = 1;
886 fullMatch = names[i];
887 break;
888 }
889
890 // Partial match?
891 if (names[i].substr(0, methodName.length()) == methodName) {
892 ++ nrOfMatches;
893 fullMatch = names[i];
894 }
895 }
896
897 stringstream ss;
898
899 if (nrOfMatches == 1) {
900 if (arguments.size() > 0) {
901 if (arguments.size() == 1 ||
902 arguments[0] != "=") {
903 // TODO: Printing expressions, such as
904 // cpu.pc + 4
905
906 m_GXemul->GetUI()->ShowDebugMessage(
907 "Syntax error. Variable assignment syntax"
908 " is:\n <variable> = <expression>\n");
909 return true;
910 }
911
912 const StateVariable* var =
913 component->GetVariable(fullMatch);
914 if (var == NULL) {
915 m_GXemul->GetUI()->ShowDebugMessage(
916 "Unknown variable.\n");
917 return true;
918 }
919
920 string assignment;
921 for (size_t i=1; i<arguments.size(); ++i)
922 assignment += arguments[i] + " ";
923
924 VariableAssignment(component->GeneratePath(), fullMatch, assignment);
925
926 return true;
927 }
928
929 // Print the variable's name and value:
930 ss << fullMatch;
931
932 const StateVariable* var = component->GetVariable(fullMatch);
933 if (var == NULL)
934 ss << " = (unknown variable?)";
935 else
936 ss << " = " << var->ToString();
937
938 ss << "\n";
939
940 m_GXemul->GetUI()->ShowDebugMessage(ss.str());
941
942 return true;
943 }
944
945 if (nrOfMatches > 1)
946 ss << methodName << ": ambiguous method or variable name of "
947 << component->GeneratePath() << ".\n";
948 else
949 ss << methodName << ": not a method or variable of "
950 << component->GeneratePath() << ".\n";
951
952 m_GXemul->GetUI()->ShowDebugMessage(ss.str());
953
954 return false;
955 }
956
957
RunCommand(const string & command,bool * pSuccess)958 bool CommandInterpreter::RunCommand(const string& command, bool* pSuccess)
959 {
960 string commandName;
961 vector<string> arguments;
962 SplitIntoWords(command, commandName, arguments);
963
964 m_mayBeReexecuted = "";
965
966 m_GXemul->GetUI()->ShowCommandMessage(command);
967
968 // Find the command...
969 Commands::iterator it = m_commands.find(commandName);
970 if (it == m_commands.end()) {
971 // Not found? Then try to tab-complete the name...
972 string commandTabCompleted = commandName;
973 size_t tmpCursorPos = commandTabCompleted.length();
974 TabComplete(commandTabCompleted, tmpCursorPos);
975
976 // remove any trailing space(s):
977 while (commandTabCompleted.length() > 0 &&
978 commandTabCompleted[commandTabCompleted.length()-1] == ' ')
979 commandTabCompleted.erase(
980 commandTabCompleted.length() - 1);
981
982 // ... and try again:
983 it = m_commands.find(commandTabCompleted);
984 if (it == m_commands.end()) {
985 // If this is a component name [with an optional
986 // method name], then execute a method on it.
987 if (RunComponentMethod(commandName, arguments))
988 return true;
989 m_GXemul->GetUI()->ShowDebugMessage(commandName +
990 ": unknown command. Type help for help.\n");
991 return false;
992 }
993 }
994
995 if (arguments.size() != 0 && (it->second)->GetArgumentFormat() == "") {
996 m_GXemul->GetUI()->ShowDebugMessage(commandName +
997 " takes no arguments. Type help " + commandName +
998 " for help on the syntax.\n");
999 return false;
1000 }
1001
1002 // ... and execute it:
1003 bool success = (it->second)->Execute(*m_GXemul, arguments);
1004 if (pSuccess != NULL) {
1005 *pSuccess = success;
1006 } else {
1007 if (!success) {
1008 throw UnitTestFailedException("apa");
1009 }
1010 }
1011
1012 if ((it->second)->MayBeReexecutedWithoutArgs())
1013 m_mayBeReexecuted = it->first;
1014
1015 return true;
1016 }
1017
1018
GetCurrentCommandBuffer() const1019 const string& CommandInterpreter::GetCurrentCommandBuffer() const
1020 {
1021 return m_currentCommandString;
1022 }
1023
1024
1025 /*****************************************************************************/
1026
1027
1028 #ifdef WITHUNITTESTS
1029
Test_CommandInterpreter_AddKey_ReturnValue()1030 static void Test_CommandInterpreter_AddKey_ReturnValue()
1031 {
1032 GXemul gxemul;
1033 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1034
1035 UnitTest::Assert("addkey of regular char should return false",
1036 ci.AddKey('a') == false);
1037
1038 UnitTest::Assert("addkey of nul char should return false",
1039 ci.AddKey('\0') == false);
1040
1041 UnitTest::Assert("addkey of newline should return true",
1042 ci.AddKey('\n') == true);
1043
1044 UnitTest::Assert("addkey of carriage return should return true too",
1045 ci.AddKey('\r') == true);
1046 }
1047
Test_CommandInterpreter_KeyBuffer()1048 static void Test_CommandInterpreter_KeyBuffer()
1049 {
1050 GXemul gxemul;
1051 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1052
1053 UnitTest::Assert("buffer should initially be empty",
1054 ci.GetCurrentCommandBuffer() == "");
1055
1056 ci.AddKey('a'); // normal char
1057
1058 UnitTest::Assert("buffer should contain 'a'",
1059 ci.GetCurrentCommandBuffer() == "a");
1060
1061 ci.AddKey('\0'); // nul char should have no effect
1062
1063 UnitTest::Assert("buffer should still contain only 'a'",
1064 ci.GetCurrentCommandBuffer() == "a");
1065
1066 ci.AddKey('A'); // multiple chars
1067 ci.AddKey('B');
1068 UnitTest::Assert("buffer should contain 'aAB'",
1069 ci.GetCurrentCommandBuffer() == "aAB");
1070
1071 ci.AddKey('\177'); // del
1072
1073 UnitTest::Assert("buffer should contain 'aA' (del didn't work?)",
1074 ci.GetCurrentCommandBuffer() == "aA");
1075
1076 ci.AddKey('\b'); // backspace
1077
1078 UnitTest::Assert("buffer should contain 'a' again (BS didn't work)",
1079 ci.GetCurrentCommandBuffer() == "a");
1080
1081 ci.AddKey('\b');
1082
1083 UnitTest::Assert("buffer should now be empty '' again",
1084 ci.GetCurrentCommandBuffer() == "");
1085
1086 ci.AddKey('\b'); // cannot be emptier than... well... empty :)
1087
1088 UnitTest::Assert("buffer should still be empty",
1089 ci.GetCurrentCommandBuffer() == "");
1090
1091 ci.AddKey('a');
1092
1093 UnitTest::Assert("buffer should contain 'a' again",
1094 ci.GetCurrentCommandBuffer() == "a");
1095
1096 ci.AddKey('Q');
1097
1098 UnitTest::Assert("buffer should contain 'aQ'",
1099 ci.GetCurrentCommandBuffer() == "aQ");
1100
1101 ci.AddKey('\n'); // newline should execute the command
1102
1103 UnitTest::Assert("buffer should be empty after executing '\\n'",
1104 ci.GetCurrentCommandBuffer() == "");
1105
1106 ci.AddKey('Z');
1107 ci.AddKey('Q');
1108
1109 UnitTest::Assert("new command should have been possible",
1110 ci.GetCurrentCommandBuffer() == "ZQ");
1111
1112 ci.AddKey('\r'); // carriage return should work like newline
1113
1114 UnitTest::Assert("buffer should be empty after executing '\\r'",
1115 ci.GetCurrentCommandBuffer() == "");
1116 }
1117
Test_CommandInterpreter_KeyBuffer_CursorMovement()1118 static void Test_CommandInterpreter_KeyBuffer_CursorMovement()
1119 {
1120 GXemul gxemul;
1121 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1122
1123 ci.AddKey('A');
1124 ci.AddKey('B');
1125 ci.AddKey('C');
1126 ci.AddKey('D');
1127 ci.AddKey('E');
1128 UnitTest::Assert("buffer should contain 'ABCDE'",
1129 ci.GetCurrentCommandBuffer(), "ABCDE");
1130
1131 ci.AddKey('\2'); // CTRL-B should move back (left)
1132 ci.AddKey('\2');
1133 ci.AddKey('\2');
1134 UnitTest::Assert("buffer should still contain 'ABCDE'",
1135 ci.GetCurrentCommandBuffer(), "ABCDE");
1136
1137 ci.AddKey('\b');
1138 UnitTest::Assert("buffer should now contain 'ACDE'",
1139 ci.GetCurrentCommandBuffer(), "ACDE");
1140
1141 ci.AddKey('\6'); // CTRL-F should move forward (right)
1142 ci.AddKey('\6');
1143 UnitTest::Assert("buffer should still contain 'ACDE'",
1144 ci.GetCurrentCommandBuffer(), "ACDE");
1145
1146 ci.AddKey('\b');
1147 UnitTest::Assert("buffer should now contain 'ACE'",
1148 ci.GetCurrentCommandBuffer(), "ACE");
1149
1150 ci.AddKey('\1'); // CTRL-A should move to start of line
1151 UnitTest::Assert("buffer should still contain 'ACE'",
1152 ci.GetCurrentCommandBuffer(), "ACE");
1153
1154 ci.AddKey('1');
1155 ci.AddKey('2');
1156 UnitTest::Assert("buffer should now contain '12ACE'",
1157 ci.GetCurrentCommandBuffer(), "12ACE");
1158
1159 ci.AddKey('\5'); // CTRL-E should move to end of line
1160 UnitTest::Assert("buffer should still contain '12ACE'",
1161 ci.GetCurrentCommandBuffer(), "12ACE");
1162
1163 ci.AddKey('x');
1164 ci.AddKey('y');
1165 UnitTest::Assert("buffer should now contain '12ACExy'",
1166 ci.GetCurrentCommandBuffer(), "12ACExy");
1167
1168 ci.AddKey('\1'); // CTRL-A move to start of line again
1169 ci.AddKey('\6'); // CTRL-F move to the right
1170 ci.AddKey('\4'); // CTRL-D should remove character to the right
1171 UnitTest::Assert("buffer should now contain '1ACExy'",
1172 ci.GetCurrentCommandBuffer(), "1ACExy");
1173 }
1174
Test_CommandInterpreter_KeyBuffer_CtrlK()1175 static void Test_CommandInterpreter_KeyBuffer_CtrlK()
1176 {
1177 GXemul gxemul;
1178 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1179
1180 ci.AddKey('A');
1181 ci.AddKey('B');
1182 ci.AddKey('C');
1183 ci.AddKey('D');
1184 ci.AddKey('E');
1185 UnitTest::Assert("buffer should contain 'ABCDE'",
1186 ci.GetCurrentCommandBuffer(), "ABCDE");
1187
1188 ci.AddKey('\2'); // CTRL-B should move back (left)
1189 ci.AddKey('\2');
1190 UnitTest::Assert("buffer should still contain 'ABCDE'",
1191 ci.GetCurrentCommandBuffer(), "ABCDE");
1192
1193 ci.AddKey('\13'); // CTRL-K
1194 UnitTest::Assert("buffer should now contain 'ABC'",
1195 ci.GetCurrentCommandBuffer(), "ABC");
1196
1197 ci.AddKey('X');
1198 ci.AddKey('\13'); // CTRL-K again, at end of line
1199 UnitTest::Assert("buffer should now contain 'ABCX'",
1200 ci.GetCurrentCommandBuffer(), "ABCX");
1201
1202 ci.AddKey('\1'); // CTRL-A to move to start of line
1203 ci.AddKey('\13'); // CTRL-K again, should erase everything
1204 UnitTest::Assert("buffer should now be empty",
1205 ci.GetCurrentCommandBuffer(), "");
1206 }
1207
Test_CommandInterpreter_KeyBuffer_CtrlW()1208 static void Test_CommandInterpreter_KeyBuffer_CtrlW()
1209 {
1210 GXemul gxemul;
1211 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1212
1213 UnitTest::Assert("buffer should contain ''",
1214 ci.GetCurrentCommandBuffer(), "");
1215 ci.AddKey('\27'); // CTRL-W
1216 UnitTest::Assert("buffer should still contain ''",
1217 ci.GetCurrentCommandBuffer(), "");
1218
1219 ci.AddKey('a');
1220 ci.AddKey('b');
1221 ci.AddKey('c');
1222 UnitTest::Assert("buffer should contain abc",
1223 ci.GetCurrentCommandBuffer(), "abc");
1224 ci.AddKey('\27'); // CTRL-W
1225 UnitTest::Assert("buffer should be empty again",
1226 ci.GetCurrentCommandBuffer(), "");
1227
1228 ci.AddKey(' ');
1229 ci.AddKey(' ');
1230 ci.AddKey('a');
1231 ci.AddKey('b');
1232 ci.AddKey('c');
1233 UnitTest::Assert("buffer should contain ' abc'",
1234 ci.GetCurrentCommandBuffer(), " abc");
1235 ci.AddKey('\27'); // CTRL-W
1236 UnitTest::Assert("buffer should contain only two spaces",
1237 ci.GetCurrentCommandBuffer(), " ");
1238
1239 ci.AddKey('a');
1240 ci.AddKey('b');
1241 ci.AddKey('c');
1242 ci.AddKey(' ');
1243 UnitTest::Assert("buffer should contain ' abc '",
1244 ci.GetCurrentCommandBuffer(), " abc ");
1245 ci.AddKey('\27'); // CTRL-W
1246 UnitTest::Assert("buffer should again contain only two spaces",
1247 ci.GetCurrentCommandBuffer(), " ");
1248
1249 ci.AddKey('a');
1250 ci.AddKey('b');
1251 ci.AddKey('c');
1252 ci.AddKey('d');
1253 ci.AddKey(' ');
1254 ci.AddKey('e');
1255 ci.AddKey('f');
1256 ci.AddKey('g');
1257 ci.AddKey('h');
1258 ci.AddKey('i');
1259 ci.AddKey(' ');
1260 ci.AddKey('\2'); // CTRL-B = move left
1261 ci.AddKey('\2');
1262 ci.AddKey('\2');
1263 UnitTest::Assert("buffer should contain ' abcd efghi '",
1264 ci.GetCurrentCommandBuffer(), " abcd efghi ");
1265 ci.AddKey('\27'); // CTRL-W
1266 UnitTest::Assert("buffer should now contain ' abcd hi '",
1267 ci.GetCurrentCommandBuffer(), " abcd hi ");
1268 }
1269
Test_CommandInterpreter_CommandHistory()1270 static void Test_CommandInterpreter_CommandHistory()
1271 {
1272 GXemul gxemul;
1273 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1274
1275 UnitTest::Assert("history should still be empty (1)",
1276 ci.AddLineToCommandHistory(""), 0);
1277
1278 UnitTest::Assert("history should still be empty (2)",
1279 ci.AddLineToCommandHistory(""), 0);
1280
1281 UnitTest::Assert("A: history line 0",
1282 ci.GetHistoryLine(0), "");
1283 UnitTest::Assert("A: history line 1 not set yet",
1284 ci.GetHistoryLine(1), "");
1285 UnitTest::Assert("A: history line 2 not set yet",
1286 ci.GetHistoryLine(2), "");
1287
1288 UnitTest::Assert("history should contain one entry",
1289 ci.AddLineToCommandHistory("hello"), 1);
1290
1291 UnitTest::Assert("B: history line 0",
1292 ci.GetHistoryLine(0), "");
1293 UnitTest::Assert("B: history line 1",
1294 ci.GetHistoryLine(1), "hello");
1295 UnitTest::Assert("B: history line 2 not set yet",
1296 ci.GetHistoryLine(2), "");
1297
1298 UnitTest::Assert("history should contain two entries",
1299 ci.AddLineToCommandHistory("world"), 2);
1300
1301 UnitTest::Assert("history should still contain two entries",
1302 ci.AddLineToCommandHistory("world"), 2);
1303
1304 UnitTest::Assert("C: history line 0",
1305 ci.GetHistoryLine(0), "");
1306 UnitTest::Assert("C: history line 1",
1307 ci.GetHistoryLine(1), "world");
1308 UnitTest::Assert("C: history line 2",
1309 ci.GetHistoryLine(2), "hello");
1310
1311 UnitTest::Assert("history should contain three entries",
1312 ci.AddLineToCommandHistory("hello"), 3);
1313
1314 UnitTest::Assert("D: history line 0",
1315 ci.GetHistoryLine(0), "");
1316 UnitTest::Assert("D: history line 1",
1317 ci.GetHistoryLine(1), "hello");
1318 UnitTest::Assert("D: history line 2",
1319 ci.GetHistoryLine(2), "world");
1320
1321 UnitTest::Assert("history should still contain three entries",
1322 ci.AddLineToCommandHistory(""), 3);
1323 }
1324
1325 /**
1326 * \brief A dummy Command, for unit testing purposes
1327 */
1328 class DummyCommand2
1329 : public Command
1330 {
1331 public:
DummyCommand2(int & valueRef)1332 DummyCommand2(int& valueRef)
1333 : Command("dummycommand", "[args]")
1334 , m_value(valueRef)
1335 {
1336 }
1337
~DummyCommand2()1338 ~DummyCommand2()
1339 {
1340 }
1341
Execute(GXemul & gxemul,const vector<string> & arguments)1342 bool Execute(GXemul& gxemul, const vector<string>& arguments)
1343 {
1344 m_value ++;
1345 return true;
1346 }
1347
GetShortDescription() const1348 string GetShortDescription() const
1349 {
1350 return "A dummy command used for unit testing.";
1351 }
1352
GetLongDescription() const1353 string GetLongDescription() const
1354 {
1355 return "This is just a dummy command used for unit testing.";
1356 }
1357
1358 private:
1359 int& m_value;
1360 };
1361
1362 /**
1363 * \brief A dummy Command, for unit testing purposes
1364 */
1365 class DummyCommand3
1366 : public Command
1367 {
1368 public:
DummyCommand3(int & valueRef)1369 DummyCommand3(int& valueRef)
1370 : Command("dummycmd", "[args]")
1371 , m_value(valueRef)
1372 {
1373 }
1374
~DummyCommand3()1375 ~DummyCommand3()
1376 {
1377 }
1378
Execute(GXemul & gxemul,const vector<string> & arguments)1379 bool Execute(GXemul& gxemul, const vector<string>& arguments)
1380 {
1381 m_value ++;
1382 return true;
1383 }
1384
GetShortDescription() const1385 string GetShortDescription() const
1386 {
1387 return "A dummy command used for unit testing.";
1388 }
1389
GetLongDescription() const1390 string GetLongDescription() const
1391 {
1392 return "This is just a dummy command used for unit testing.";
1393 }
1394
1395 private:
1396 int& m_value;
1397 };
1398
Test_CommandInterpreter_AddCommand()1399 static void Test_CommandInterpreter_AddCommand()
1400 {
1401 GXemul gxemul;
1402 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1403
1404 size_t nCommands = ci.GetCommands().size();
1405 UnitTest::Assert("there should be some commands already",
1406 nCommands > 0);
1407
1408 ci.AddCommand(new VersionCommand);
1409
1410 UnitTest::Assert("it should not be possible to have multiple commands"
1411 " with the same name",
1412 ci.GetCommands().size() == nCommands);
1413
1414 int dummyInt = 42;
1415 ci.AddCommand(new DummyCommand2(dummyInt));
1416
1417 UnitTest::Assert("it should be possible to add new commands",
1418 ci.GetCommands().size() == nCommands + 1);
1419 }
1420
Test_CommandInterpreter_TabCompletion_EmptyLine()1421 static void Test_CommandInterpreter_TabCompletion_EmptyLine()
1422 {
1423 GXemul gxemul;
1424 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1425
1426 ci.AddKey('\t');
1427 UnitTest::Assert("tab completion should not have produced anything",
1428 ci.GetCurrentCommandBuffer(), "");
1429 }
1430
Test_CommandInterpreter_TabCompletion_FullWord()1431 static void Test_CommandInterpreter_TabCompletion_FullWord()
1432 {
1433 GXemul gxemul;
1434 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1435
1436 ci.AddKey('d');
1437 ci.AddKey('u');
1438 ci.AddKey('m');
1439 ci.AddKey('m');
1440 ci.AddKey('Z');
1441 ci.AddKey('\2'); // CTRL-B = move left
1442 UnitTest::Assert("initial buffer contents mismatch",
1443 ci.GetCurrentCommandBuffer(), "dummZ");
1444
1445 ci.AddKey('\t');
1446 UnitTest::Assert("tab completion should have failed",
1447 ci.GetCurrentCommandBuffer(), "dummZ");
1448
1449 int dummyInt = 42;
1450 ci.AddCommand(new DummyCommand2(dummyInt));
1451
1452 ci.AddKey('\t');
1453 UnitTest::Assert("tab completion should have succeeded",
1454 ci.GetCurrentCommandBuffer(), "dummycommandZ");
1455
1456 ci.AddKey('X');
1457 UnitTest::Assert("tab completion should have placed cursor at end of"
1458 " the tab-completed word",
1459 ci.GetCurrentCommandBuffer(), "dummycommandXZ");
1460 }
1461
Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()1462 static void Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()
1463 {
1464 GXemul gxemul;
1465 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1466
1467 ci.AddKey(' ');
1468 ci.AddKey(' ');
1469 ci.AddKey('v');
1470 ci.AddKey('e');
1471 ci.AddKey('r');
1472 ci.AddKey('s');
1473 UnitTest::Assert("initial buffer contents mismatch",
1474 ci.GetCurrentCommandBuffer(), " vers");
1475
1476 ci.AddKey('\t');
1477 UnitTest::Assert("tab completion should have succeeded",
1478 ci.GetCurrentCommandBuffer(), " version ");
1479 }
1480
Test_CommandInterpreter_TabCompletion_Partial()1481 static void Test_CommandInterpreter_TabCompletion_Partial()
1482 {
1483 GXemul gxemul;
1484 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1485
1486 ci.AddKey('d');
1487 ci.AddKey('u');
1488 ci.AddKey('m');
1489 ci.AddKey('m');
1490 ci.AddKey('Z');
1491 ci.AddKey('\2'); // CTRL-B = move left
1492 UnitTest::Assert("initial buffer contents mismatch",
1493 ci.GetCurrentCommandBuffer(), "dummZ");
1494
1495 int dummyInt = 42;
1496 ci.AddCommand(new DummyCommand2(dummyInt));
1497 ci.AddCommand(new DummyCommand3(dummyInt));
1498
1499 ci.AddKey('\t');
1500 UnitTest::Assert("tab completion should have partially succeeded",
1501 ci.GetCurrentCommandBuffer(), "dummycZ");
1502 }
1503
Test_CommandInterpreter_TabCompletion_C()1504 static void Test_CommandInterpreter_TabCompletion_C()
1505 {
1506 GXemul gxemul;
1507 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1508
1509 ci.AddKey('c');
1510 UnitTest::Assert("initial buffer contents mismatch",
1511 ci.GetCurrentCommandBuffer(), "c");
1512
1513 ci.AddKey('\t');
1514 UnitTest::Assert("tab completion should not have modified command",
1515 ci.GetCurrentCommandBuffer(), "c");
1516
1517 // ... because there are at least two possible commands on the
1518 // letter c: close and continue.
1519 }
1520
Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()1521 static void Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()
1522 {
1523 GXemul gxemul;
1524 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1525
1526 ci.AddKey('v');
1527 ci.AddKey('e');
1528 ci.AddKey('r');
1529 ci.AddKey('s');
1530 UnitTest::Assert("initial buffer contents mismatch",
1531 ci.GetCurrentCommandBuffer(), "vers");
1532
1533 ci.AddKey('\t');
1534 UnitTest::Assert("first tab completion should have succeeded",
1535 ci.GetCurrentCommandBuffer(), "version ");
1536
1537 ci.AddKey('v');
1538 ci.AddKey('e');
1539 ci.AddKey('r');
1540 ci.AddKey('s');
1541 UnitTest::Assert("buffer contents mismatch",
1542 ci.GetCurrentCommandBuffer(), "version vers");
1543
1544 ci.AddKey('\t');
1545 UnitTest::Assert("second tab completion should have failed",
1546 ci.GetCurrentCommandBuffer(), "version vers");
1547 }
1548
Test_CommandInterpreter_TabCompletion_ComponentName()1549 static void Test_CommandInterpreter_TabCompletion_ComponentName()
1550 {
1551 GXemul gxemul;
1552 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1553
1554 ci.RunCommand("add testm88k");
1555 UnitTest::Assert("initial buffer should be empty",
1556 ci.GetCurrentCommandBuffer(), "");
1557
1558 ci.AddKey('c');
1559 ci.AddKey('p');
1560 ci.AddKey('\t');
1561 UnitTest::Assert("tab completion should have completed the "
1562 "component name",
1563 ci.GetCurrentCommandBuffer(), "cpu0");
1564
1565 // Note: No space after component tab completion.
1566 }
1567
Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()1568 static void Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()
1569 {
1570 GXemul gxemul;
1571 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1572
1573 ci.RunCommand("add testm88k");
1574 UnitTest::Assert("initial buffer should be empty",
1575 ci.GetCurrentCommandBuffer(), "");
1576
1577 ci.AddKey('c');
1578 ci.AddKey('p');
1579 ci.AddKey('u');
1580 ci.AddKey('0');
1581 ci.AddKey('\t');
1582 UnitTest::Assert("tab completion should not have changed anything",
1583 ci.GetCurrentCommandBuffer(), "cpu0");
1584
1585 // Note: No space after component tab completion.
1586 }
1587
Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()1588 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()
1589 {
1590 GXemul gxemul;
1591 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1592
1593 ci.RunCommand("add testm88k");
1594 ci.RunCommand("add m88k_cpu mainbus0");
1595 UnitTest::Assert("initial buffer should be empty",
1596 ci.GetCurrentCommandBuffer(), "");
1597
1598 ci.AddKey('c');
1599 ci.AddKey('p');
1600 ci.AddKey('\t');
1601 UnitTest::Assert("there are both cpu0 and cpu1, so don't expand all the way",
1602 ci.GetCurrentCommandBuffer(), "cpu");
1603
1604 // Note: No space after component tab completion.
1605 }
1606
Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()1607 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()
1608 {
1609 GXemul gxemul;
1610 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1611
1612 ci.RunCommand("add testm88k"); // root.machine0
1613 ci.RunCommand("add testm88k"); // root.machine1
1614 UnitTest::Assert("initial buffer should be empty",
1615 ci.GetCurrentCommandBuffer(), "");
1616
1617 ci.AddKey('c');
1618 ci.AddKey('p');
1619 ci.AddKey('\t');
1620 UnitTest::Assert("there are cpu0 in both root.machine0 and root.machine1",
1621 ci.GetCurrentCommandBuffer(), "machine");
1622
1623 // Note: No space after component tab completion.
1624 }
1625
Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()1626 static void Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()
1627 {
1628 GXemul gxemul;
1629 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1630
1631 ci.RunCommand("add dummy");
1632 UnitTest::Assert("initial buffer should be empty",
1633 ci.GetCurrentCommandBuffer(), "");
1634
1635 ci.AddKey('r');
1636 ci.AddKey('o');
1637 ci.AddKey('o');
1638 ci.AddKey('t');
1639 ci.AddKey('.');
1640 ci.AddKey('X');
1641 ci.AddKey('\t');
1642 UnitTest::Assert("tab completion should not have succeeded",
1643 ci.GetCurrentCommandBuffer(), "root.X");
1644 }
1645
Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()1646 static void Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()
1647 {
1648 GXemul gxemul;
1649 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1650
1651 ci.RunCommand("add dummy root");
1652 UnitTest::Assert("initial buffer should be empty",
1653 ci.GetCurrentCommandBuffer(), "");
1654
1655 ci.AddKey('a');
1656 ci.AddKey('d');
1657 ci.AddKey('d');
1658 ci.AddKey(' ');
1659 ci.AddKey('d');
1660 ci.AddKey('u');
1661 ci.AddKey('m');
1662 ci.AddKey('m');
1663 ci.AddKey('y');
1664 ci.AddKey(' ');
1665 ci.AddKey('d');
1666 ci.AddKey('u');
1667 UnitTest::Assert("buffer contents mismatch",
1668 ci.GetCurrentCommandBuffer(), "add dummy du");
1669
1670 ci.AddKey('\t');
1671 UnitTest::Assert("tab completion should have completed the "
1672 "component name",
1673 ci.GetCurrentCommandBuffer(), "add dummy dummy0");
1674
1675 // Note: No space after component tab completion.
1676 }
1677
Test_CommandInterpreter_TabCompletion_CWithComponents()1678 static void Test_CommandInterpreter_TabCompletion_CWithComponents()
1679 {
1680 GXemul gxemul;
1681 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1682
1683 ci.RunCommand("add testm88k");
1684
1685 ci.AddKey('c');
1686 UnitTest::Assert("initial buffer contents mismatch",
1687 ci.GetCurrentCommandBuffer(), "c");
1688
1689 ci.AddKey('\t');
1690 UnitTest::Assert("tab completion should not have modified command",
1691 ci.GetCurrentCommandBuffer(), "c");
1692
1693 // ... because there are at least two possible commands on the
1694 // letter c: close and continue.
1695 }
1696
Test_CommandInterpreter_TabCompletion_roWithComponents()1697 static void Test_CommandInterpreter_TabCompletion_roWithComponents()
1698 {
1699 GXemul gxemul;
1700 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1701
1702 ci.RunCommand("add testm88k");
1703
1704 ci.AddKey('r');
1705 ci.AddKey('o');
1706 ci.AddKey('\t');
1707 // there are both "rom0" and "root".
1708 UnitTest::Assert("tab completion should not have expanded",
1709 ci.GetCurrentCommandBuffer(), "ro");
1710
1711 ci.AddKey('o');
1712 ci.AddKey('\t');
1713 UnitTest::Assert("tab completion should have expanded to 'root'",
1714 ci.GetCurrentCommandBuffer(), "root");
1715 }
1716
Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()1717 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()
1718 {
1719 GXemul gxemul;
1720 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1721
1722 ci.RunCommand("add testm88k");
1723
1724 ci.AddKey('c');
1725 ci.AddKey('p');
1726 ci.AddKey('u');
1727 ci.AddKey('.');
1728 ci.AddKey('\t');
1729 UnitTest::Assert("tab completion should have caused expansion",
1730 ci.GetCurrentCommandBuffer(), "cpu0.");
1731 }
1732
Test_CommandInterpreter_TabCompletion_ComponentMethods()1733 static void Test_CommandInterpreter_TabCompletion_ComponentMethods()
1734 {
1735 GXemul gxemul;
1736 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1737
1738 ci.RunCommand("add testm88k");
1739
1740 ci.AddKey('c');
1741 ci.AddKey('p');
1742 ci.AddKey('u');
1743 ci.AddKey('.');
1744 ci.AddKey('u');
1745 ci.AddKey('\t');
1746 UnitTest::Assert("tab completion should have caused expansion",
1747 ci.GetCurrentCommandBuffer(), "cpu0.unassemble");
1748 }
1749
Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()1750 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()
1751 {
1752 GXemul gxemul;
1753 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1754
1755 ci.RunCommand("add testm88k");
1756
1757 ci.AddKey('c');
1758 ci.AddKey('p');
1759 ci.AddKey('u');
1760 ci.AddKey('.');
1761 ci.AddKey('u');
1762 ci.AddKey('n');
1763 ci.AddKey('a');
1764 ci.AddKey('b');
1765 ci.AddKey('c');
1766 ci.AddKey('d');
1767 ci.AddKey('\2');
1768 ci.AddKey('\2');
1769 ci.AddKey('\2'); // cursor placed after "una"
1770 ci.AddKey('\t');
1771 UnitTest::Assert("tab completion should have caused expansion",
1772 ci.GetCurrentCommandBuffer(), "cpu0.unassemblebcd");
1773 }
1774
Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()1775 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()
1776 {
1777 GXemul gxemul;
1778 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1779
1780 ci.RunCommand("add testm88k");
1781
1782 ci.AddKey('c');
1783 ci.AddKey('p');
1784 ci.AddKey('u');
1785 ci.AddKey('.');
1786 ci.AddKey('u');
1787 ci.AddKey(' ');
1788 ci.AddKey('a');
1789 ci.AddKey('d');
1790 ci.AddKey('d');
1791 ci.AddKey('r');
1792 ci.AddKey('\2');
1793 ci.AddKey('\2');
1794 ci.AddKey('\2');
1795 ci.AddKey('\2');
1796 ci.AddKey('\2'); // cursor placed after "u"
1797 ci.AddKey('\t');
1798 UnitTest::Assert("tab completion should have caused expansion",
1799 ci.GetCurrentCommandBuffer(), "cpu0.unassemble addr");
1800 }
1801
Test_CommandInterpreter_TabCompletion_ComponentVariables()1802 static void Test_CommandInterpreter_TabCompletion_ComponentVariables()
1803 {
1804 GXemul gxemul;
1805 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1806
1807 ci.RunCommand("add testm88k");
1808
1809 ci.AddKey('c');
1810 ci.AddKey('p');
1811 ci.AddKey('u');
1812 ci.AddKey('.');
1813 ci.AddKey('s');
1814 ci.AddKey('h');
1815 ci.AddKey('\t');
1816 UnitTest::Assert("tab completion should have caused expansion cpu -> cpu0",
1817 ci.GetCurrentCommandBuffer(), "cpu0.showFunctionTrace");
1818 }
1819
Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()1820 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()
1821 {
1822 GXemul gxemul;
1823 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1824
1825 ci.RunCommand("add ram");
1826
1827 ci.AddKey('r');
1828 ci.AddKey('a');
1829 ci.AddKey('m');
1830 ci.AddKey('.');
1831 ci.AddKey('m');
1832 ci.AddKey('e');
1833 ci.AddKey('m');
1834 ci.AddKey('o');
1835 ci.AddKey('\t');
1836 UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1837 ci.GetCurrentCommandBuffer(), "ram0.memoryMapped");
1838 }
1839
Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()1840 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()
1841 {
1842 GXemul gxemul;
1843 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1844
1845 ci.RunCommand("add ram");
1846
1847 ci.AddKey('r');
1848 ci.AddKey('a');
1849 ci.AddKey('m');
1850 ci.AddKey('.');
1851 ci.AddKey('m');
1852 ci.AddKey('e');
1853 ci.AddKey('m');
1854 ci.AddKey('o');
1855 ci.AddKey(' ');
1856 ci.AddKey('2');
1857 ci.AddKey('\2');
1858 ci.AddKey('\2');
1859 ci.AddKey('\t');
1860 UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1861 ci.GetCurrentCommandBuffer(), "ram0.memoryMapped 2");
1862
1863 ci.AddKey('X');
1864 UnitTest::Assert("cursor position after tab completion was wrong?",
1865 ci.GetCurrentCommandBuffer(), "ram0.memoryMappedX 2");
1866 }
1867
Test_CommandInterpreter_NonExistingCommand()1868 static void Test_CommandInterpreter_NonExistingCommand()
1869 {
1870 GXemul gxemul;
1871 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1872
1873 UnitTest::Assert("nonexisting (nonsense) command should fail",
1874 ci.RunCommand("nonexistingcommand") == false);
1875 }
1876
Test_CommandInterpreter_SimpleCommand()1877 static void Test_CommandInterpreter_SimpleCommand()
1878 {
1879 GXemul gxemul;
1880 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1881
1882 UnitTest::Assert("simple command should succeed",
1883 ci.RunCommand("version") == true);
1884
1885 UnitTest::Assert("simple command with whitespace should succeed",
1886 ci.RunCommand(" version ") == true);
1887 }
1888
Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()1889 static void Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()
1890 {
1891 GXemul gxemul;
1892 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1893
1894 UnitTest::Assert("simple command should succeed",
1895 ci.RunCommand("version") == true);
1896
1897 UnitTest::Assert("simple command which does not take arguments should"
1898 " fail when attempt is made to execute it with arguments",
1899 ci.RunCommand("version hello") == false);
1900 }
1901
Test_CommandInterpreter_ComponentMethods()1902 static void Test_CommandInterpreter_ComponentMethods()
1903 {
1904 GXemul gxemul;
1905 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1906
1907 UnitTest::Assert("Huh? Could not add testm88k.",
1908 ci.RunCommand("add testm88k") == true);
1909
1910 UnitTest::Assert("component method 1",
1911 ci.RunCommand("cpu") == true);
1912 UnitTest::Assert("component method 2",
1913 ci.RunCommand("cpu.u") == true);
1914 UnitTest::Assert("component method 3",
1915 ci.RunCommand("cpu.urk") == false);
1916 UnitTest::Assert("component method 4",
1917 ci.RunCommand("cpu.unassemble") == true);
1918 UnitTest::Assert("component method 5",
1919 ci.RunCommand("root.machine0.mainbus0.cpu") == true);
1920 UnitTest::Assert("component method 6",
1921 ci.RunCommand("root.machine0.mainbus0.cpu0") == true);
1922 UnitTest::Assert("component method 7",
1923 ci.RunCommand("root.machine0.mainbus0.cpu.u") == true);
1924 UnitTest::Assert("component method 8",
1925 ci.RunCommand("root.machine0.mainbus0.cpu0.unassemble") == true);
1926 }
1927
Test_CommandInterpreter_ComponentVariables_NoArgs()1928 static void Test_CommandInterpreter_ComponentVariables_NoArgs()
1929 {
1930 GXemul gxemul;
1931 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1932
1933 UnitTest::Assert("Huh? Could not add testm88k.",
1934 ci.RunCommand("add testm88k") == true);
1935
1936 UnitTest::Assert("component variable 1",
1937 ci.RunCommand("cpu.nonexistant") == false);
1938 UnitTest::Assert("component variable 2",
1939 ci.RunCommand("cpu.pau") == true);
1940 UnitTest::Assert("component variable 3",
1941 ci.RunCommand("root.machine0.mainbus0.cpu0.pau") == true);
1942 }
1943
Test_CommandInterpreter_ComponentVariables_Ambiguous()1944 static void Test_CommandInterpreter_ComponentVariables_Ambiguous()
1945 {
1946 GXemul gxemul;
1947 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1948
1949 UnitTest::Assert("Huh? Could not add testmips.",
1950 ci.RunCommand("add testm88k") == true);
1951
1952 UnitTest::Assert("cpu.f should not work, there should be multiple matches",
1953 ci.RunCommand("cpu.f") == false);
1954 }
1955
Test_CommandInterpreter_ComponentVariables_PartialIsOk()1956 static void Test_CommandInterpreter_ComponentVariables_PartialIsOk()
1957 {
1958 GXemul gxemul;
1959 CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1960
1961 UnitTest::Assert("Huh? Could not add testm88k.",
1962 ci.RunCommand("add testm88k") == true);
1963
1964 // Make sure that r2 works, and doesn't complain about ambiguity.
1965 UnitTest::Assert("gpr 2",
1966 ci.RunCommand("cpu.r2") == true);
1967 UnitTest::Assert("gpr 29",
1968 ci.RunCommand("cpu.r29") == true);
1969 }
1970
UNITTESTS(CommandInterpreter)1971 UNITTESTS(CommandInterpreter)
1972 {
1973 // Key and current buffer:
1974 UNITTEST(Test_CommandInterpreter_AddKey_ReturnValue);
1975 UNITTEST(Test_CommandInterpreter_KeyBuffer);
1976 UNITTEST(Test_CommandInterpreter_KeyBuffer_CursorMovement);
1977 UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlK);
1978 UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlW);
1979
1980 // Command History:
1981 UNITTEST(Test_CommandInterpreter_CommandHistory);
1982
1983 // AddCommand / GetCommands:
1984 UNITTEST(Test_CommandInterpreter_AddCommand);
1985
1986 // Tab completion:
1987 UNITTEST(Test_CommandInterpreter_TabCompletion_EmptyLine);
1988 UNITTEST(Test_CommandInterpreter_TabCompletion_FullWord);
1989 UNITTEST(Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine);
1990 UNITTEST(Test_CommandInterpreter_TabCompletion_Partial);
1991 UNITTEST(Test_CommandInterpreter_TabCompletion_C);
1992 UNITTEST(Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord);
1993 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentName);
1994 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete);
1995 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultiple);
1996 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents);
1997 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameNonexist);
1998 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument);
1999 UNITTEST(Test_CommandInterpreter_TabCompletion_CWithComponents);
2000 UNITTEST(Test_CommandInterpreter_TabCompletion_roWithComponents);
2001 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty);
2002 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods);
2003 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle);
2004 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg);
2005 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables);
2006 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max);
2007 UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2);
2008
2009 // RunCommand:
2010 UNITTEST(Test_CommandInterpreter_NonExistingCommand);
2011 UNITTEST(Test_CommandInterpreter_SimpleCommand);
2012 UNITTEST(Test_CommandInterpreter_SimpleCommand_NoArgsAllowed);
2013 UNITTEST(Test_CommandInterpreter_ComponentMethods);
2014 UNITTEST(Test_CommandInterpreter_ComponentVariables_NoArgs);
2015 UNITTEST(Test_CommandInterpreter_ComponentVariables_Ambiguous);
2016 UNITTEST(Test_CommandInterpreter_ComponentVariables_PartialIsOk);
2017 }
2018
2019 #endif
2020