Asserts

From LSDevLinux
Revision as of 23:18, 4 August 2012 by Mayhewn (talk | contribs) (Reverted edits by LindaHayes (Talk) to last revision by Mayhewn)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Asserts help identify problems in the code. They only show up in debug builds, i.e. the end user who will get a release build doesn't see them. When running on a developer's machine the desired behavior if an assertion fails is to display a message box so that the developer can break into the debugger. On a machine that runs automated builds we don't want to pop up message boxes but silently log the assertion.

Managed Code

In C# asserts are done with: System.Diagnostics.Debug.Assert(condition)

Windows/.NET

If the assert fails, by default .NET will display a message box with the options Abort, Retry and Cancel.

The behavior can be changed in the configuration file:

Changing it in the machine.config file will change it for any application for all users on the current machine (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config). Another option is to change it in the application.config (e.g. TE.exe.config) file.

.NET doesn't provide a way to change the setting on a per-user base. Therefore we added a custom trace listener in FieldWorks (Src/Utilities/BasicUtils/EnvVarTraceListener.cs) that reads its setting from an environment variable. To make use of the custom trace listener, add the following to the test's or application's App.config file:

   <system.diagnostics>
       <trace autoflush="true" indentsize="4">
           <listeners>
               <add name="FwTraceListener" type="SIL.Utils.EnvVarTraceListener, BasicUtils, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
                   initializeData="assertuienabled='true' logfilename='%temp%/asserts.log'"/>
           </listeners>
       </trace>
   </system.diagnostics>

The default values can be set in the initializedData attribute. Anything between percent signs in logfilename will be replaced by its corresponding environment variable.

The default value for assertuienabled can be overridden by the environment variable AssertUiEnabled. If an assertion fails and assertuienabled is set to false, an AssertionFailedException will be thrown unless the environment varibale AssertExceptionEnabled is set to false.

Linux/Mono

Mono by default ignores assertions. To get it to display a message box you have to change one of the config files mentioned above.

The custom trace listener can also be used as described above.

Unmanaged Code

In FieldWorks we define our set of Assert methods (in Generic/debug.h). If an assertion fails the method AssertProc gets called. Since this is a function pointer it can be set to a custom method.

For assertions that happen in unamanged code called from a managed executable, we call DebugProcs.AssertProc (in Common/CoreImpl/DebugProcs.cs) which calls System.Diagnostics.Debug.Fail(), so the remarks from above apply.

For assertions that happen in unmanaged code called from an unmanaged executable, we call SilAssert (in DebugProcs/DebugProcs.cpp). This method either displays a message box similar to the managed one, or raises the SIGABRT signal.

We use an enhanced version of the unit++ testing framework for our C++ tests that treats tests that get a SIGABRT signal as failed, but continues with the remaining tests. This is useful when not displaying a message box. However, IF we display a message box in case of a failed assertion and the developer presses the Abort button, the test application quits without running the remaining tests.

To determine whether to display a message box or not we look at the registry value HKLM\Software\SIL\FieldWorks\AssertMessageBox on a Windows machine. If that value doesn't exist or if we're running on Linux we check the environment variable AssertUiEnabled.

On Linux we use the xmessage tool to display a message box. This can be changed to display a more modern dialog by setting the environment variable XMESSAGE, e.g. XMESSAGE=gmessage