ee1a107c35ee8a147530c86a722bbf163e88033b
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System;
6 using System.Diagnostics;
7 using System.Runtime.Serialization;
8
9 namespace Stress.Data
10 {
11     public enum ErrorHandlingAction
12     {
13         // If you add an item here, remember to add it to all of the methods below
14         DebugBreak,
15         ThrowException
16     }
17
18     /// <summary>
19     /// Static class containing methods to report errors.
20     ///
21     /// The StressTest executor will eat exceptions that are thrown and write them out to the console. In theory these should all be
22     /// either harmless exceptions or product bugs, however at present there are a large number of test issues that will cause a flood
23     /// of exceptions. Therefore if something actually bad happens (e.g. a known product bug is hit due to regression, or a major test
24     /// programming error) this error would be easy to miss if it were reported just by throwing an exception. To solve this, we use
25     /// this class for structured & consistent handling of errors.
26     /// </summary>
27     public static class DataStressErrors
28     {
29         private static void DebugBreak(string message, Exception exception)
30         {
31             // Print out the error before breaking to make debugging easier
32             Console.WriteLine(message);
33             if (exception != null)
34             {
35                 Console.WriteLine(exception);
36             }
37
38             Debugger.Break();
39         }
40
41         /// <summary>
42         /// Reports that a product bug has been hit. The action that will be taken is configurable in the .config file.
43         /// This can be used to check for regressions of known product bugs.
44         /// </summary>
45         /// <param name="description">A description of the product bug hit (e.g. title, bug number & database, more information)</param>
46         /// <param name="exception">The exception that was thrown that indicates a product bug, or null if the product bug was detected without
47         /// having thrown an exception</param>
48         /// <returns>An exception that the caller should throw.</returns>
49         public static Exception ProductError(string description, Exception exception = null)
50         {
51             switch (DataStressSettings.Instance.ActionOnProductError)
52             {
53                 case ErrorHandlingAction.DebugBreak:
54                     DebugBreak("Hit product error: " + description, exception);
55                     return new ProductErrorException(description, exception);
56
57                 case ErrorHandlingAction.ThrowException:
58                     return new ProductErrorException(description, exception);
59
60                 default:
61                     throw UnhandledCaseError(DataStressSettings.Instance.ActionOnProductError);
62             }
63         }
64
65         /// <summary>
66         /// Reports that a non-fatal test error has been hit. The action that will be taken is configurable in the .config file.
67         /// This should be used for test errors that do not prevent the test from running.
68         /// </summary>
69         /// <param name="description">A description of the error</param>
70         /// <param name="exception">The exception that was thrown that indicates an error, or null if the error was detected without
71         /// <returns>An exception that the caller should throw.</returns>
72         public static Exception TestError(string description, Exception exception = null)
73         {
74             switch (DataStressSettings.Instance.ActionOnTestError)
75             {
76                 case ErrorHandlingAction.DebugBreak:
77                     DebugBreak("Hit test error: " + description, exception);
78                     return new TestErrorException(description, exception);
79
80                 case ErrorHandlingAction.ThrowException:
81                     return new TestErrorException(description, exception);
82
83                 default:
84                     throw UnhandledCaseError(DataStressSettings.Instance.ActionOnTestError);
85             }
86         }
87
88         /// <summary>
89         /// Reports that a programming error in the test code has occurred. The action that will be taken is configurable in the .config file.
90         /// This must strictly be used to report programming errors. It should not be in any way possible to see one of these errors unless
91         /// you make an incorrect change to the code, for example having an unhandled case in a switch statement.
92         /// </summary>
93         /// <param name="description">A description of the error</param>
94         /// <param name="exception">The exception that was thrown that indicates an error, or null if the error was detected without
95         /// having thrown an exception</param>
96         /// <returns>An exception that the caller should throw.</returns>
97         private static Exception ProgrammingError(string description, Exception exception = null)
98         {
99             switch (DataStressSettings.Instance.ActionOnProgrammingError)
100             {
101                 case ErrorHandlingAction.DebugBreak:
102                     DebugBreak("Hit programming error: " + description, exception);
103                     return new ProgrammingErrorException(description, exception);
104
105                 case ErrorHandlingAction.ThrowException:
106                     return new ProgrammingErrorException(description, exception);
107
108                 default:
109                     // If we are here then it's a programming error, but calling UnhandledCaseError here would cause an inifite loop.
110                     goto case ErrorHandlingAction.DebugBreak;
111             }
112         }
113
114         /// <summary>
115         /// Reports that an unhandled case in a switch statement in the test code has occurred. The action that will be taken is configurable
116         /// as a programming error in the .config file. It should not be in any way possible to see one of these errors unless
117         /// you make an incorrect change to the test code, for example having an unhandled case in a switch statement.
118         /// </summary>
119         /// <param name="unhandledValue">The value that was not handled in the switch statement</param>
120         /// <returns>An exception that the caller should throw.</returns>
121         public static Exception UnhandledCaseError<T>(T unhandledValue)
122         {
123             return ProgrammingError("Unhandled case in switch statement: " + unhandledValue);
124         }
125
126         /// <summary>
127         /// Asserts that a condition is true. If the condition is false then throws a ProgrammingError.
128         /// This must strictly be used to report programming errors. It should not be in any way possible to see one of these errors unless
129         /// you make an incorrect change to the code, for example having an unhandled case in a switch statement.
130         /// </summary>
131         /// <param name="condition">A condition to assert</param>
132         /// <param name="description">A description of the error</param>
133         /// <exception cref="ProgrammingErrorException">if the condition is false</exception>
134         public static void Assert(bool condition, string description)
135         {
136             if (!condition)
137             {
138                 throw ProgrammingError(description);
139             }
140         }
141
142         /// <summary>
143         /// Reports that a fatal error has happened. This is an error that completely prevents the test from continuing,
144         /// for example a setup failure. Ordinary programming errors should not be handled by this method.
145         /// </summary>
146         /// <param name="description">A description of the error</param>
147         /// <returns>An exception that the caller should throw.</returns>
148         public static Exception FatalError(string description)
149         {
150             Console.WriteLine("Fatal test error: {0}", description);
151             Debugger.Break();       // Give the user a chance to debug
152             Environment.FailFast("Fatal error. Exit.");
153             return new Exception(); // Caller should throw this to indicate to the compiler that any code after the call is unreachable
154         }
155
156         #region Exception types
157
158         // These exception types are provided so that they can be easily found in logs, i.e. just do a text search in the console
159         // output log for "ProductErrorException"
160
161         private class ProductErrorException : Exception
162         {
163             public ProductErrorException()
164                 : base()
165             {
166             }
167
168             public ProductErrorException(string message)
169                 : base(message)
170             {
171             }
172
173             public ProductErrorException(string message, Exception innerException)
174                 : base(message, innerException)
175             {
176             }
177         }
178
179         private class ProgrammingErrorException : Exception
180         {
181             public ProgrammingErrorException()
182                 : base()
183             {
184             }
185
186             public ProgrammingErrorException(string message)
187                 : base(message)
188             {
189             }
190
191             public ProgrammingErrorException(string message, Exception innerException)
192                 : base(message, innerException)
193             {
194             }
195         }
196
197         private class TestErrorException : Exception
198         {
199             public TestErrorException()
200                 : base()
201             {
202             }
203
204             public TestErrorException(string message)
205                 : base(message)
206             {
207             }
208
209             public TestErrorException(string message, Exception innerException)
210                 : base(message, innerException)
211             {
212             }
213         }
214         #endregion
215     }
216 }