/// <summary> /// Runs the specified action for all combinations of the specified combinatorial engine. /// </summary> /// <param name="engine">The engine to use.</param> /// <param name="action">Action to call for each combination.</param> /// <param name="skipTo">If specified determines which combination to skip to. Must be null when running outside the debugger.</param> public static void RunCombinations(this ICombinatorialEngine engine, Action <Dictionary <string, object> > action, int?skipTo = null) { if (skipTo.HasValue && !Debugger.IsAttached) { throw new ArgumentException("The skipTo parameter was used in RunCombinations and the code is not running under debugger. " + "The skipTo is only intended for usage during debugging and MUST NOT be present in checked-in code."); } int combinationNumber = 0; while (engine.NextCombination()) { combinationNumber++; if (skipTo.HasValue && skipTo > combinationNumber) { continue; } try { engine.LogCombinationNumber(combinationNumber); action(engine.CurrentDimensionValues); } catch (Exception e) { string message = string.Format( "Failure in combination #{1} for dimension values:{0}{2}{0}", Environment.NewLine, combinationNumber.ToString(), engine.DescribeDimensions()); throw new Exception(message, e); } } }
/// <summary> /// Writes the current dimensions and their values of the specified engine to a string for logging purposes. /// </summary> /// <param name="engine">The combinatorial engine to write values for.</param> /// <returns>String representation suitable for logging.</returns> public static string DescribeDimensions(this ICombinatorialEngine engine) { if (engine.CurrentDimensionValues == null) { return("[none]"); } StringBuilder sb = new StringBuilder(); bool first = true; foreach (var dimension in engine.Dimensions) { if (!first) { sb.AppendLine(); } first = false; sb.Append(dimension.Name); sb.Append(": ["); object dimensionValue = engine.CurrentDimensionValues[dimension.Name]; if (dimensionValue == null) { dimensionValue = "*null*"; } else if (dimensionValue is string[]) { dimensionValue = "string[]:" + string.Join(",", (string[])dimensionValue); } sb.Append(dimensionValue); sb.Append("]"); } return(sb.ToString()); }
/// <summary> /// Runs combinations using specified engine provider. /// </summary> /// <param name="provider">The engine provider to use to create the combinatorial engine.</param> /// <param name="action">The action to run for each combination.</param> /// <param name="skipTo">If specified determines which combination to skip to. Must be null when running outside the debugger.</param> /// <param name="dimensions">Array of enumerations which represent the dimensions, these are matched by order to the parameters of the action.</param> public static void RunCombinations(this ICombinatorialEngineProvider provider, Delegate action, int?skipTo, params IEnumerable[] dimensions) { ParameterInfo[] parameters = action.Method.GetParameters(); CombinatorialDimension[] dimensionInstances = dimensions.Select((dimensionEnumerable, dimensionIndex) => new CombinatorialDimension(parameters[dimensionIndex].Name, dimensionEnumerable)).ToArray(); ICombinatorialEngine engine = provider.CreateEngine(dimensionInstances); engine.RunCombinations((dimensionValues) => { action.DynamicInvoke(parameters.Select(parameter => dimensionValues[parameter.Name]).ToArray()); }, skipTo); }