Example #1
0
        /// <summary>
        /// Generates source code for the specified test case, assembles it, and compares
        /// the output of both steps to expected values.  The process is repeated for every
        /// known assembler.
        ///
        /// If an assembler is known but not configured, the assembly step is skipped, and
        /// does not count as a failure.
        /// </summary>
        /// <param name="pathName">Full path to test case.</param>
        /// <returns>True if all assemblers worked as expected.</returns>
        private bool GenerateAndAssemble(string pathName)
        {
            ReportProgress(Path.GetFileName(pathName) + "...\r\n");

            // Create DisasmProject object, either as a new project for a plain data file,
            // or from a project file.
            DisasmProject project = InstantiateProject(pathName,
                                                       out FileLoadReport projectLoadReport);

            if (project == null)
            {
                ReportFailure();
                return(false);
            }

            int testNum = GetTestNum(pathName);

            // Create a temporary directory to work in.
            string workDir = CreateWorkDirectory(pathName);

            if (string.IsNullOrEmpty(workDir))
            {
                ReportFailure();
                project.Cleanup();
                return(false);
            }

            AppSettings settings = CreateNormalizedSettings();

            ApplyProjectSettings(settings, project);

            // Iterate through all known assemblers.
            bool didFail = false;

            foreach (AssemblerInfo.Id asmId in
                     (AssemblerInfo.Id[])Enum.GetValues(typeof(AssemblerInfo.Id)))
            {
                if (asmId == AssemblerInfo.Id.Unknown)
                {
                    continue;
                }

                string    fileName = Path.GetFileName(pathName);
                TaskTimer timer    = new TaskTimer();
                timer.StartTask("Full Test Duration");

                // Create results object and add it to the list.  We'll add stuff to it for
                // as far as we get.
                GenTestResults results = new GenTestResults(pathName, asmId);
                mResults.Add(results);
                results.ProjectLoadReport = projectLoadReport;

                // Generate source code.
                ReportProgress("  " + asmId.ToString() + " generate...");
                IGenerator gen = AssemblerInfo.GetGenerator(asmId);
                if (gen == null)
                {
                    ReportErrMsg("generator unavailable");
                    ReportProgress("\r\n");
                    //didFail = true;
                    continue;
                }
                timer.StartTask("Generate Source");
                gen.Configure(project, workDir, fileName,
                              AssemblerVersionCache.GetVersion(asmId), settings);
                List <string> genPathNames = gen.GenerateSource(mWorker);
                timer.EndTask("Generate Source");
                if (mWorker.CancellationPending)
                {
                    // The generator will stop early if a cancellation is requested.  If we
                    // don't break here, the compare function will report a failure, which
                    // isn't too problematic but looks funny.
                    break;
                }

                ReportProgress(" verify...");
                timer.StartTask("Compare Source to Expected");
                bool match = CompareGeneratedToExpected(pathName, genPathNames);
                timer.EndTask("Compare Source to Expected");
                if (match)
                {
                    ReportSuccess();
                    results.GenerateOkay = true;
                }
                else
                {
                    ReportFailure();
                    didFail = true;

                    // The fact that it doesn't match the expected sources doesn't mean it's
                    // invalid.  Go ahead and try to build it.
                    //continue;
                }

                // Assemble code.
                ReportProgress("  " + asmId.ToString() + " assemble...");
                IAssembler asm = AssemblerInfo.GetAssembler(asmId);
                if (asm == null)
                {
                    ReportErrMsg("assembler unavailable");
                    ReportProgress("\r\n");
                    continue;
                }

                timer.StartTask("Assemble Source");
                asm.Configure(genPathNames, workDir);
                AssemblerResults asmResults = asm.RunAssembler(mWorker);
                timer.EndTask("Assemble Source");
                if (asmResults == null)
                {
                    ReportErrMsg("unable to run assembler");
                    ReportFailure();
                    didFail = true;
                    continue;
                }
                if (asmResults.ExitCode != 0)
                {
                    ReportErrMsg("assembler returned code=" + asmResults.ExitCode);
                    ReportFailure();
                    didFail = true;
                    continue;
                }

                results.AsmResults = asmResults;

                ReportProgress(" verify...");
                timer.StartTask("Compare Binary to Expected");
                FileInfo fi = new FileInfo(asmResults.OutputPathName);
                if (!fi.Exists)
                {
                    // This can happen if the assembler fails to generate output but doesn't
                    // report an error code (e.g. Merlin 32 in certain situations).
                    ReportErrMsg("asm output missing");
                    ReportFailure();
                    didFail = true;
                    continue;
                }
                else if (fi.Length != project.FileData.Length)
                {
                    ReportErrMsg("asm output mismatch: length is " + fi.Length + ", expected " +
                                 project.FileData.Length);
                    ReportFailure();
                    didFail = true;
                    continue;
                }
                else if (!FileUtil.CompareBinaryFile(project.FileData, asmResults.OutputPathName,
                                                     out int badOffset, out byte badFileVal))
                {
                    ReportErrMsg("asm output mismatch: offset +" + badOffset.ToString("x6") +
                                 " has value $" + badFileVal.ToString("x2") + ", expected $" +
                                 project.FileData[badOffset].ToString("x2"));
                    ReportFailure();
                    didFail = true;
                    continue;
                }
                timer.EndTask("Compare Binary to Expected");

                // Victory!
                results.AssembleOkay = true;
                ReportSuccess();

                timer.EndTask("Full Test Duration");
                results.Timer = timer;

                // We don't scrub the directory on success at this point.  We could, but we'd
                // need to remove only those files associated with the currently assembler.
                // Otherwise, a failure followed by a success would wipe out the unsuccessful
                // temporaries.
            }

            // If something failed, leave the bits around for examination.  Otherwise, try to
            // remove the directory and all its contents.
            if (!didFail && !RetainOutput)
            {
                ScrubWorkDirectory(workDir, testNum);
                RemoveWorkDirectory(workDir);
            }

            project.Cleanup();
            return(!didFail);
        }
Example #2
0
        /// <summary>
        /// Applies the changes to the project, and updates the display.
        ///
        /// This is called by the undo/redo commands.  Don't call this directly from the
        /// various UI-driven functions, as this does not add the change to the undo stack.
        /// </summary>
        /// <param name="cs">Set of changes to apply.</param>
        /// <param name="backward">If set, undo the changes instead.</param>
        private void ApplyChanges(ChangeSet cs, bool backward)
        {
            mReanalysisTimer.Clear();
            mReanalysisTimer.StartTask("ProjectView.ApplyChanges()");

            mReanalysisTimer.StartTask("Save selection");
#if false
            int topItem = codeListView.TopItem.Index;
#else
            int topItem = 0;
#endif
            int topOffset = mDisplayList[topItem].FileOffset;
            DisplayListGen.SavedSelection savedSel = DisplayListGen.SavedSelection.Generate(
                mDisplayList, mCodeViewSelection, topOffset);
            //savedSel.DebugDump();
            mReanalysisTimer.EndTask("Save selection");

            mReanalysisTimer.StartTask("Apply changes");
            UndoableChange.ReanalysisScope needReanalysis = mProject.ApplyChanges(cs, backward,
                                                                                  out RangeSet affectedOffsets);
            mReanalysisTimer.EndTask("Apply changes");

            string refreshTaskStr = "Refresh w/reanalysis=" + needReanalysis;
            mReanalysisTimer.StartTask(refreshTaskStr);
            if (needReanalysis != UndoableChange.ReanalysisScope.None)
            {
                Debug.WriteLine("Refreshing project (" + needReanalysis + ")");
                RefreshProject(needReanalysis);
            }
            else
            {
                Debug.WriteLine("Refreshing " + affectedOffsets.Count + " offsets");
                RefreshCodeListViewEntries(affectedOffsets);
                mProject.Validate();    // shouldn't matter w/o reanalysis, but do it anyway
            }
            mReanalysisTimer.EndTask(refreshTaskStr);

            VirtualListViewSelection newSel = savedSel.Restore(mDisplayList, out int topIndex);
            //newSel.DebugDump();

            // Refresh the various windows, and restore the selection.
            mReanalysisTimer.StartTask("Invalidate controls");
#if false
            InvalidateControls(newSel);
#endif
            mReanalysisTimer.EndTask("Invalidate controls");

            // This apparently has to be done after the EndUpdate, and inside try/catch.
            // See https://stackoverflow.com/questions/626315/ for notes.
            try {
                Debug.WriteLine("Setting TopItem to index=" + topIndex);
#if false
                codeListView.TopItem = codeListView.Items[topIndex];
#endif
            } catch (NullReferenceException) {
                Debug.WriteLine("Caught an NRE from TopItem");
            }

            mReanalysisTimer.EndTask("ProjectView.ApplyChanges()");

            //mReanalysisTimer.DumpTimes("ProjectView timers:", mGenerationLog);
#if false
            if (mShowAnalysisTimersDialog != null)
            {
                string timerStr = mReanalysisTimer.DumpToString("ProjectView timers:");
                mShowAnalysisTimersDialog.BodyText = timerStr;
            }
#endif

            // Lines may have moved around.  Update the selection highlight.  It's important
            // we do it here, and not down in DoRefreshProject(), because at that point the
            // ListView's selection index could be referencing a line off the end.
#if false
            UpdateSelectionHighlight();
#endif
        }