├── .gitattributes
├── .gitignore
├── BuildAndDeployment
├── Legacy
│ ├── Build.Project.Pro.bat
│ ├── Build.Project.bat
│ ├── EditNuspec.msbuild
│ └── Viki.LoadRunner.Tools.Legacy.nuspec
└── nuget.exe
├── LICENSE
├── README.md
├── Viki.LoadRunner.sln
├── demo
├── App.config
├── Common
│ ├── SleepingScenario.cs
│ └── SleepingScenarioFactory.cs
├── DemoResults.xlsx
├── Guides
│ ├── Aggregation
│ │ ├── HistogramAggregatorDemo.cs
│ │ └── RawDataMeasurementsDemo.cs
│ ├── QuickStart
│ │ └── QuickStartDemo.cs
│ └── StrategyBuilderFeatures
│ │ └── ScenarioFactoryDemo.cs
├── Legacy
│ ├── Detailed
│ │ ├── Aggregation.cs
│ │ ├── DetailedDemo.cs
│ │ ├── Scenario.cs
│ │ └── Strategy.cs
│ └── Features
│ │ └── RawDataAggregation.cs
├── LoadRunner.Demo.csproj
├── LoadRunner.Demo.sln
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── Theoretical
│ ├── AggregationImpactDemo.cs
│ ├── CountingScenario.cs
│ ├── CountingScenarioFactory.cs
│ └── TheoreticalSpeedDemo.cs
└── packages.config
├── diagrams
└── Architecture.png
└── src
├── Viki.LoadRunner.Engine
├── Aggregators
│ ├── Dimensions
│ │ ├── FuncDimension.cs
│ │ └── TimeDimension.cs
│ ├── FileStreamAggregatorBase.cs
│ ├── HistogramAggregator.cs
│ ├── Interfaces
│ │ ├── IDimension.cs
│ │ └── IMetric.cs
│ ├── Metrics
│ │ ├── AvgDurationMetric.cs
│ │ ├── BreakByMetric.cs
│ │ ├── CheckpointValuesMetric.cs
│ │ ├── CountMetric.cs
│ │ ├── ErrorCountMetric.cs
│ │ ├── ErrorRatioMetric.cs
│ │ ├── FuncMetric.cs
│ │ ├── FuncMultiMetric.cs
│ │ ├── GlobalTimerMaxValueMetric.cs
│ │ ├── GlobalTimerMinValueMetric.cs
│ │ ├── GlobalTimerPeriodMetric.cs
│ │ ├── MaxDurationMetric.cs
│ │ ├── MetricBase.cs
│ │ ├── MinDurationMetric.cs
│ │ ├── MultiMetricBase.cs
│ │ ├── PercentileMetric.cs
│ │ └── TransactionsPerSecMetric.cs
│ ├── Result
│ │ └── HistogramResult.cs
│ ├── StreamAggregator.cs
│ ├── StreamAggregatorBase.cs
│ └── Utils
│ │ ├── OrderLearner.cs
│ │ ├── ReplayResult.cs
│ │ ├── ResultExtensions.cs
│ │ └── UnixDateTimeExtensions.cs
├── Analytics
│ ├── DimensionKey.cs
│ ├── Dimensions
│ │ ├── FuncDimension.cs
│ │ ├── ThresholdDimension.cs
│ │ └── TimeDimension.cs
│ ├── DimensionsHandler.cs
│ ├── Extensions
│ │ └── HistogramExtensions.cs
│ ├── FlexiRow.cs
│ ├── HistogramBase.cs
│ ├── HistogramBuilderExtensions.cs
│ ├── Interfaces
│ │ ├── IDimension.cs
│ │ ├── IHistogramBuilder.cs
│ │ └── IMetric.cs
│ ├── Metrics
│ │ ├── AverageMetric.cs
│ │ ├── Calculators
│ │ │ ├── AverageCalculator.cs
│ │ │ └── RatioCalculator.cs
│ │ ├── CountMetric.cs
│ │ ├── Delegates.cs
│ │ ├── DistinctCountMetric.cs
│ │ ├── DistinctListCountMetric.cs
│ │ ├── DistinctListMetric.cs
│ │ ├── FilterMetric.cs
│ │ ├── FuncMetric.cs
│ │ ├── MaxMetric.cs
│ │ ├── MinMetric.cs
│ │ ├── PercentileMetric.cs
│ │ ├── RatioMetric.cs
│ │ ├── SubDimension.cs
│ │ ├── SubMetric.cs
│ │ ├── SumMetric.cs
│ │ └── ValuesMetric.cs
│ ├── MetricsHandler.cs
│ └── MetricsTemplate.cs
├── Core
│ ├── Collector
│ │ ├── AggregatorException.cs
│ │ ├── Interfaces
│ │ │ ├── IAggregator.cs
│ │ │ ├── IDataCollector.cs
│ │ │ └── IResult.cs
│ │ ├── IterationResult.cs
│ │ ├── NullDataCollector.cs
│ │ ├── PipeDataCollector.cs
│ │ ├── Pipeline
│ │ │ ├── BatchingPipe.cs
│ │ │ ├── Extensions
│ │ │ │ ├── ConsumerExtensions.cs
│ │ │ │ └── ProducerExtensions.cs
│ │ │ ├── Interfaces
│ │ │ │ ├── IConsumer.cs
│ │ │ │ ├── IPipeFactory.cs
│ │ │ │ └── IProducer.cs
│ │ │ ├── PipeFactory.cs
│ │ │ ├── PipeMultiplexer.cs
│ │ │ ├── PipeMultiplier.cs
│ │ │ └── PipeMuxer.cs
│ │ └── PipelineDataAggregator.cs
│ ├── Counter
│ │ ├── Interfaces
│ │ │ ├── ICounter.cs
│ │ │ └── IThreadPoolCounter.cs
│ │ ├── ThreadPoolCounter.cs
│ │ └── ThreadSafeCounter.cs
│ ├── Factory
│ │ ├── Interfaces
│ │ │ ├── FuncScenarioFactory.cs
│ │ │ ├── IDataCollectorFactory.cs
│ │ │ ├── IFactory.cs
│ │ │ ├── IIterationContextFactory.cs
│ │ │ ├── IScenarioFactory.cs
│ │ │ ├── IScenarioHandlerFactory.cs
│ │ │ ├── IScenarioThreadFactory.cs
│ │ │ └── ISchedulerFactory.cs
│ │ ├── IterationContextFactory.cs
│ │ ├── NullDataCollectorFactory.cs
│ │ ├── NullSchedulerFactory.cs
│ │ ├── PipeDataCollectorFactory.cs
│ │ ├── ReflectionFactory.cs
│ │ ├── ScenarioFactory.cs
│ │ ├── ScenarioHandlerFactory.cs
│ │ ├── ScenarioThreadFactory.cs
│ │ ├── SchedulerFactory.cs
│ │ └── ThreadFactory.cs
│ ├── Generator
│ │ ├── Interfaces
│ │ │ └── IUniqueIdGenerator.cs
│ │ ├── MockedIdGenerator.cs
│ │ ├── NotThreadSafeIdGenerator.cs
│ │ └── ThreadSafeIdGenerator.cs
│ ├── Pool
│ │ ├── Interfaces
│ │ │ ├── IThreadFactory.cs
│ │ │ ├── IThreadPool.cs
│ │ │ └── IThreadPoolStats.cs
│ │ ├── ThreadPool.cs
│ │ └── WorkerThreadStats.cs
│ ├── Scenario
│ │ ├── Checkpoint.cs
│ │ ├── CheckpointExtensions.cs
│ │ ├── GlobalCounters.cs
│ │ ├── Interfaces
│ │ │ ├── ICheckpoint.cs
│ │ │ ├── IGlobalCounters.cs
│ │ │ ├── IGlobalCountersControl.cs
│ │ │ ├── IIteration.cs
│ │ │ ├── IIterationControl.cs
│ │ │ ├── IIterationId.cs
│ │ │ ├── IIterationMetadata.cs
│ │ │ ├── IIterationResult.cs
│ │ │ ├── IScenario.cs
│ │ │ └── IScenarioHandler.cs
│ │ ├── IterationContext.cs
│ │ ├── ScenarioBase.cs
│ │ └── ScenarioHandler.cs
│ ├── Scheduler
│ │ ├── Interfaces
│ │ │ ├── IScheduler.cs
│ │ │ └── IWait.cs
│ │ ├── NullScheduler.cs
│ │ ├── Scheduler.cs
│ │ └── SemiWait.cs
│ ├── State
│ │ ├── Interfaces
│ │ │ └── ITestState.cs
│ │ └── TestState.cs
│ ├── Timer
│ │ ├── ExecutionTimer.cs
│ │ ├── Interfaces
│ │ │ ├── ITimer.cs
│ │ │ └── ITimerControl.cs
│ │ └── StopWatchEx.cs
│ └── Worker
│ │ ├── ErrorHandler.cs
│ │ ├── Interfaces
│ │ ├── IErrorHandler.cs
│ │ ├── IPrewait.cs
│ │ ├── IThread.cs
│ │ └── IWork.cs
│ │ ├── NullPrewait.cs
│ │ ├── ScenarioWork.cs
│ │ ├── TimerBasedPrewait.cs
│ │ ├── WorkerException.cs
│ │ └── WorkerThread.cs
├── Interfaces
│ ├── IStrategyExecutor.cs
│ └── IStrategyExecutorAsync.cs
├── LoadRunnerEngine.cs
├── Strategies
│ ├── Custom
│ │ ├── Adapter
│ │ │ ├── Limit
│ │ │ │ └── LimitsHandler.cs
│ │ │ └── Speed
│ │ │ │ ├── ScheduleTable.cs
│ │ │ │ └── SlowestSpeedStrategy.cs
│ │ ├── CustomStrategy.cs
│ │ ├── Factory
│ │ │ └── PriorityStrategyFactory.cs
│ │ ├── Interfaces
│ │ │ └── ICustomStrategySettings.cs
│ │ └── Strategies
│ │ │ ├── Interfaces
│ │ │ ├── ILimitStrategy.cs
│ │ │ ├── ISpeedStrategy.cs
│ │ │ └── IThreadingStrategy.cs
│ │ │ ├── Limit
│ │ │ ├── ErrorLimit.cs
│ │ │ ├── IterationLimit.cs
│ │ │ └── TimeLimit.cs
│ │ │ ├── Speed
│ │ │ ├── BatchBySlowestSpeed.cs
│ │ │ ├── BatchByTimeIntervalSpeed.cs
│ │ │ ├── ClockedListOfSpeed.cs
│ │ │ ├── FixedSpeed.cs
│ │ │ ├── IncrementalLimitWorkingThreads.cs
│ │ │ ├── IncrementalSpeed.cs
│ │ │ ├── LimitWorkingThreads.cs
│ │ │ ├── ListOfSpeed.cs
│ │ │ └── MaxSpeed.cs
│ │ │ └── Threading
│ │ │ ├── FixedThreadCount.cs
│ │ │ ├── IncrementalThreadCount.cs
│ │ │ └── ListOfCounts.cs
│ ├── Extensions
│ │ ├── FeatureExtensions.cs
│ │ ├── ReplayDataReaderExtensions.cs
│ │ └── StrategyBuilderExtensions.cs
│ ├── Interfaces
│ │ ├── IAggregatorFeature.cs
│ │ ├── IStrategy.cs
│ │ ├── IStrategyBuilder.cs
│ │ ├── ITimeoutFeature.cs
│ │ └── IUserDataFeature.cs
│ ├── Replay
│ │ ├── Data
│ │ │ ├── DataItem.cs
│ │ │ ├── Interfaces
│ │ │ │ └── IReplayDataReader.cs
│ │ │ └── Readers
│ │ │ │ ├── PartitionedDataReader.cs
│ │ │ │ └── ReplayDataReader.cs
│ │ ├── Factory
│ │ │ ├── Interfaces
│ │ │ │ ├── IReplayScenarioFactory.cs
│ │ │ │ ├── IReplayScenarioHandlerFactory.cs
│ │ │ │ └── IReplaySchedulerFactory.cs
│ │ │ ├── ReplayScenarioFactory.cs
│ │ │ ├── ReplayScenarioHandlerFactory.cs
│ │ │ ├── ReplayScenarioThreadFactory.cs
│ │ │ └── ReplaySchedulerFactory.cs
│ │ ├── Interfaces
│ │ │ ├── IReplayScenario.cs
│ │ │ └── IReplayStrategySettings.cs
│ │ ├── ReplayStrategy.cs
│ │ ├── Scenario
│ │ │ ├── Interfaces
│ │ │ │ └── IReplayScenarioHandler.cs
│ │ │ └── ReplayScenarioHandler.cs
│ │ └── Scheduler
│ │ │ ├── DataContext.cs
│ │ │ ├── Interfaces
│ │ │ └── IDataContext.cs
│ │ │ └── ReplayScheduler.cs
│ ├── ReplayStrategyBuilder.cs
│ └── StrategyBuilder.cs
├── Utils
│ ├── ArrayExtensions.cs
│ ├── EnumerableExtensions.cs
│ └── StringExtensions.cs
├── Validators
│ ├── IValidator.cs
│ ├── ReplayScenarioValidator.cs
│ ├── ReplayScenarioValidatorExtensions.cs
│ ├── ScenarioValidator.cs
│ └── ScenarioValidatorExtensions.cs
└── Viki.LoadRunner.Engine.csproj
├── Viki.LoadRunner.Playground
├── App.config
├── AssertPipeline.cs
├── BatchAndWaitDemo.cs
├── BatchStrategy.cs
├── BlankScenario.cs
├── BlankStressScenarioJsonStream.cs
├── BlankStressScenarioMemoryStream.cs
├── DemoSetup.cs
├── FaultyAggregator.cs
├── LimitConcurrencyAndTpsDemo.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── Replay
│ ├── DataGenerator.cs
│ ├── ReplayDemo.cs
│ └── ReplayScenario.cs
├── RnD
│ └── PercentileMetricWithCount.cs
├── TheoreticalSpeedDemo.cs
├── Tools
│ ├── AssertIterationIdsAggregator.cs
│ ├── CountingScenario.cs
│ └── CountingScenarioFactory.cs
├── Viki.LoadRunner.Playground.csproj
└── packages.config
├── Viki.LoadRunner.Tools.Legacy
├── Extensions
│ ├── ControlExtensions.cs
│ └── StrategyBuilderExtensions.cs
├── Properties
│ └── AssemblyInfo.cs
├── Viki.LoadRunner.Tools.Legacy.csproj
├── Windows
│ ├── LoadRunnerUi.Designer.cs
│ ├── LoadRunnerUi.cs
│ └── LoadRunnerUi.resx
└── packages.config
└── Viki.LoadRunner.Tools
├── Aggregators
├── BsonStreamAggregator.cs
├── FileStreamAggregator.cs
└── JsonStreamAggregator.cs
├── Analytics
└── Histogram.cs
├── ConsoleUi
└── KpiPrinterAggregator.cs
├── Extensions
├── CsvStream.cs
├── DictionaryExtensions.cs
└── JsonStream.cs
├── Strategy
├── GenericWork.cs
└── StrategyBase.cs
└── Viki.LoadRunner.Tools.csproj
/BuildAndDeployment/Legacy/Build.Project.Pro.bat:
--------------------------------------------------------------------------------
1 | Build.Project.bat Professional
--------------------------------------------------------------------------------
/BuildAndDeployment/Legacy/EditNuspec.msbuild:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | @(FileLines,'%0d%0a')
11 | $(FileContents.Replace('version%3E%3C', 'version%3E$(majorVersion).$(minorVersion)%3C'))
12 | $(FileContents.Replace('id%3E%3C', 'id%3E$(projectID).$(majorVersion)%3C'))
13 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/BuildAndDeployment/Legacy/Viki.LoadRunner.Tools.Legacy.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LoadRunner Tools Legacy
5 | Viki.LoadRunner.Tools.Legacy
6 |
7 | Vytautas Klumbys
8 |
9 | This library only contains Winforms BuildUi() as it is unsupported in .NET Standard
10 | Legacy features cut from Tools nuget, as they not work under .NET Standard
11 | false
12 |
13 | https://github.com/Vycka/LoadRunner/blob/master/LICENSE
14 | https://github.com/Vycka/LoadRunner
15 | 2019
16 | Load Stress Performance Test Testing c# Runner Parallel Thread Library Framework Tool Addin .NET
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/BuildAndDeployment/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vycka/LoadRunner/ccca980dd144c729f025d5c1b2125b62a64a0d48/BuildAndDeployment/nuget.exe
--------------------------------------------------------------------------------
/demo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/demo/Common/SleepingScenario.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
4 |
5 | namespace LoadRunner.Demo.Common
6 | {
7 | public class SleepingScenario : IScenario
8 | {
9 | private readonly TimeSpan _sleepTime;
10 |
11 | public SleepingScenario(TimeSpan sleepTime)
12 | {
13 | _sleepTime = sleepTime;
14 | }
15 |
16 | public void ScenarioSetup(IIteration context)
17 | {
18 | }
19 |
20 | public void IterationSetup(IIteration context)
21 | {
22 | }
23 |
24 | public void ExecuteScenario(IIteration context)
25 | {
26 | Thread.Sleep(_sleepTime);
27 | }
28 |
29 | public void IterationTearDown(IIteration context)
30 | {
31 | }
32 |
33 | public void ScenarioTearDown(IIteration context)
34 | {
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/demo/Common/SleepingScenarioFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Core.Factory.Interfaces;
3 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
4 |
5 | namespace LoadRunner.Demo.Common
6 | {
7 | public class SleepingScenarioFactory : IScenarioFactory
8 | {
9 | private readonly TimeSpan _sleepTime;
10 |
11 | public SleepingScenarioFactory(TimeSpan sleepTime)
12 | {
13 | _sleepTime = sleepTime;
14 | }
15 |
16 | public IScenario Create(int threadId)
17 | {
18 | return new SleepingScenario(_sleepTime);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/demo/DemoResults.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vycka/LoadRunner/ccca980dd144c729f025d5c1b2125b62a64a0d48/demo/DemoResults.xlsx
--------------------------------------------------------------------------------
/demo/LoadRunner.Demo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2024
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadRunner.Demo", "LoadRunner.Demo.csproj", "{E3DA725E-873C-4CDC-9DCD-272A8D6FF7A3}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {E3DA725E-873C-4CDC-9DCD-272A8D6FF7A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {E3DA725E-873C-4CDC-9DCD-272A8D6FF7A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {E3DA725E-873C-4CDC-9DCD-272A8D6FF7A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {E3DA725E-873C-4CDC-9DCD-272A8D6FF7A3}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {E900CC96-4402-4B65-9B9F-E8337AB0A14E}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/demo/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("LoadRunner.Demo")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("LoadRunner.Demo")]
12 | [assembly: AssemblyCopyright("Copyright © 2015")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("e3da725e-873c-4cdc-9dcd-272a8d6ff7a3")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/demo/Theoretical/CountingScenario.cs:
--------------------------------------------------------------------------------
1 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
2 |
3 | namespace LoadRunner.Demo.Theoretical
4 | {
5 | public class CountingScenario : IScenario
6 | {
7 | public int Count = 0;
8 | public int ThreadId;
9 |
10 | public void ScenarioSetup(IIteration context)
11 | {
12 | ThreadId = context.ThreadId;
13 | }
14 |
15 | public void IterationSetup(IIteration context)
16 | {
17 |
18 | }
19 |
20 | public void ExecuteScenario(IIteration context)
21 | {
22 | Count = Count + 1;
23 | }
24 |
25 | public void IterationTearDown(IIteration context)
26 | {
27 |
28 | }
29 |
30 | public void ScenarioTearDown(IIteration context)
31 | {
32 |
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/demo/Theoretical/CountingScenarioFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Viki.LoadRunner.Engine.Core.Factory.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
6 |
7 | namespace LoadRunner.Demo.Theoretical
8 | {
9 | public class CountingScenarioFactory : IScenarioFactory
10 | {
11 | List _instances = new List();
12 |
13 | public IScenario Create(int threadId)
14 | {
15 | CountingScenario scenario = new CountingScenario();
16 | _instances.Add(scenario);
17 |
18 | return scenario;
19 | }
20 |
21 | public void PrintSum()
22 | {
23 | string perThread = String.Join(Environment.NewLine, _instances.Select(i => $"{i.ThreadId} {i.Count}"));
24 | int sum = GetSum();
25 |
26 | Console.WriteLine(perThread);
27 | Console.WriteLine(@"-------");
28 | Console.WriteLine(sum);
29 | }
30 |
31 |
32 | public int GetSum()
33 | {
34 | return _instances.Sum(i => i.Count);
35 | }
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/demo/Theoretical/TheoreticalSpeedDemo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using Viki.LoadRunner.Engine;
4 | using Viki.LoadRunner.Engine.Strategies;
5 | using Viki.LoadRunner.Engine.Strategies.Custom.Strategies.Limit;
6 | using Viki.LoadRunner.Engine.Strategies.Custom.Strategies.Threading;
7 | using Viki.LoadRunner.Engine.Strategies.Extensions;
8 |
9 | namespace LoadRunner.Demo.Theoretical
10 | {
11 |
12 | // No aggregation is used here, worker threads them selves will count how much iterations they have done and all will be just summed up.
13 | public static class TheoreticalSpeedDemo
14 | {
15 | public static void Run()
16 | {
17 | CountingScenarioFactory scenarioFactory = new CountingScenarioFactory();
18 |
19 | StrategyBuilder strategy = new StrategyBuilder()
20 | .SetScenario(scenarioFactory)
21 | .SetThreading(new FixedThreadCount(4))
22 | .SetLimit(new TimeLimit(TimeSpan.FromSeconds(10)));
23 |
24 | // Increase TimeLimit precision
25 | LoadRunnerEngine engine = strategy.Build();
26 | engine.HeartBeatMs = 1;
27 |
28 | Stopwatch watch = new Stopwatch();
29 | watch.Start();
30 | engine.Run();
31 | watch.Stop();
32 |
33 | scenarioFactory.PrintSum();
34 | Console.WriteLine($@"TPS {scenarioFactory.GetSum() / watch.Elapsed.TotalSeconds:N}");
35 | Console.WriteLine(watch.Elapsed.ToString("g"));
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/demo/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/diagrams/Architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vycka/LoadRunner/ccca980dd144c729f025d5c1b2125b62a64a0d48/diagrams/Architecture.png
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Dimensions/FuncDimension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Dimensions
7 | {
8 | ///
9 | /// Split results by provided Func
10 | ///
11 | public class FuncDimension : IDimension
12 | {
13 | private readonly Func _dimensionValueSelector;
14 |
15 | /// Name/Key of custom dimension
16 | /// Dimension value selector
17 | public FuncDimension(string dimensionName, Func dimensionValueSelector)
18 | {
19 | if (dimensionName == null)
20 | throw new ArgumentNullException(nameof(dimensionName));
21 | if (dimensionValueSelector == null)
22 | throw new ArgumentNullException(nameof(dimensionValueSelector));
23 |
24 | _dimensionValueSelector = dimensionValueSelector;
25 | DimensionName = dimensionName;
26 | }
27 |
28 | public string DimensionName { get; }
29 |
30 | string IDimension.GetKey(IResult result)
31 | {
32 | return _dimensionValueSelector(result);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Dimensions/TimeDimension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Dimensions
7 | {
8 | ///
9 | /// Split results in provided time intervals
10 | ///
11 | public class TimeDimension : IDimension
12 | {
13 | public readonly TimeSpan Interval;
14 |
15 | public Func TimeSelector = r => r.IterationFinished;
16 | public Func Formatter = t => ((long) t.TotalSeconds).ToString();
17 |
18 | /// interval timespan
19 | /// Custom name for dimension
20 | public TimeDimension(TimeSpan interval, string dimensionName = "Time (s)")
21 | {
22 | Interval = interval;
23 | DimensionName = dimensionName;
24 | }
25 |
26 | public string DimensionName { get; }
27 |
28 | string IDimension.GetKey(IResult result)
29 | {
30 | TimeSpan resultTimeSlot = Calculate(Interval, TimeSelector(result));
31 |
32 | return Formatter(resultTimeSlot);
33 | }
34 |
35 | ///
36 | /// Calculates TimeSpan value for dimension key.
37 | ///
38 | public static TimeSpan Calculate(TimeSpan interval, TimeSpan time)
39 | {
40 | return TimeSpan.FromTicks(((int) (time.Ticks / interval.Ticks)) * interval.Ticks);
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/FileStreamAggregatorBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
4 |
5 | namespace Viki.LoadRunner.Engine.Aggregators
6 | {
7 | public abstract class FileStreamAggregatorBase : StreamAggregatorBase
8 | {
9 | #region Fields
10 |
11 | private readonly Func _outFileNameFunc;
12 |
13 | #endregion
14 |
15 | #region Constructors
16 |
17 | public FileStreamAggregatorBase(string jsonOutputfile) : this(() => jsonOutputfile)
18 | {
19 | }
20 |
21 | public FileStreamAggregatorBase(Func dynamicJsonOutputFile)
22 | {
23 | _outFileNameFunc = dynamicJsonOutputFile;
24 |
25 | }
26 |
27 | #endregion
28 |
29 | #region FileStreamAggregatorBase
30 |
31 | protected override void Process(IEnumerable stream)
32 | {
33 | Write(_outFileNameFunc(), stream);
34 | }
35 |
36 | #endregion
37 |
38 | #region FileStreamAggregatorBase abstracts
39 |
40 | public abstract void Write(string filePath, IEnumerable stream);
41 |
42 | #endregion
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/HistogramAggregator.cs:
--------------------------------------------------------------------------------
1 | using Viki.LoadRunner.Engine.Analytics;
2 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Aggregators
5 | {
6 | ///
7 | /// Modular 2D grid histogram aggregator/builder. Use Add() method to register concrete IDiminension's and IMetric's
8 | ///
9 | public class HistogramAggregator : HistogramBase, IAggregator
10 | {
11 | public void End()
12 | {
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Interfaces/IDimension.cs:
--------------------------------------------------------------------------------
1 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
2 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Aggregators.Interfaces
5 | {
6 | public interface IDimension : IDimension
7 | {
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Interfaces/IMetric.cs:
--------------------------------------------------------------------------------
1 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
2 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Aggregators.Interfaces
5 | {
6 | public interface IMetric : IMetric
7 | {
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/BreakByMetric.cs:
--------------------------------------------------------------------------------
1 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
2 | using Viki.LoadRunner.Engine.Analytics.Metrics;
3 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
4 |
5 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
6 | {
7 | ///
8 | /// BreakByMetric allows additional data slicing by provided sub-dimension.
9 | ///
10 | public class BreakByMetric : SubDimension
11 | {
12 | public BreakByMetric(IDimension subDimension, params IMetric[] actualMetrics)
13 | : base(subDimension, actualMetrics)
14 | {
15 | }
16 |
17 | public BreakByMetric(IDimension subDimension, ColumnNameDelegate columnNameSelector, params IMetric[] actualMetrics)
18 | : base(subDimension, columnNameSelector, actualMetrics)
19 | {
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/ErrorCountMetric.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics;
4 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
6 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
7 |
8 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
9 | {
10 | public class ErrorCountMetric : IMetric
11 | {
12 | private readonly bool _includeTotals;
13 | private readonly FlexiRow _row = new FlexiRow(() => default(int));
14 |
15 | public ErrorCountMetric(bool includeTotals = true)
16 | {
17 | _includeTotals = includeTotals;
18 |
19 | if (_includeTotals)
20 | _row.Touch("Errors: Totals");
21 | }
22 |
23 | IMetric IMetric.CreateNew()
24 | {
25 | return new ErrorCountMetric(_includeTotals);
26 | }
27 |
28 | void IMetric.Add(IResult result)
29 | {
30 |
31 | ICheckpoint[] checkpoints = result.Checkpoints;
32 | for (int i = 0, j = checkpoints.Length; i < j; i++)
33 | {
34 | ICheckpoint checkpoint = checkpoints[i];
35 | if (checkpoint.Error != null)
36 | {
37 | string key = "Errors: " + checkpoint.Name;
38 |
39 | _row[key]++;
40 |
41 | if (_includeTotals)
42 | _row["Errors: Totals"]++;
43 | }
44 | }
45 | }
46 |
47 | string[] IMetric.ColumnNames => _row.Keys.ToArray();
48 | object[] IMetric.Values => _row.Values.Select(v => (object)v).ToArray();
49 | }
50 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/FuncMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
3 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
4 |
5 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
6 | {
7 | public class FuncMetric : MetricBase
8 | {
9 | private readonly Func _metricFunc;
10 |
11 | public FuncMetric(string keyName, TValue initialValue, Func metricFunc)
12 | : base(keyName, initialValue)
13 | {
14 | _metricFunc = metricFunc;
15 | }
16 |
17 | protected override IMetric CreateNewMetric()
18 | {
19 | return new FuncMetric(_keyName, _initialValue, _metricFunc);
20 | }
21 |
22 | protected override void AddResult(IResult result)
23 | {
24 | _value = _metricFunc(_value, result);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/FuncMultiMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Analytics;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
7 | {
8 | public class FuncMultiMetric : MultiMetricBase
9 | {
10 | private readonly Action, IResult> _metricProcedure;
11 | private readonly Func _cellBuilderFunc;
12 |
13 | public FuncMultiMetric(Action, IResult> metricProcedure, Func cellBuilderFunc)
14 | : base(cellBuilderFunc)
15 | {
16 | if (metricProcedure == null) throw new ArgumentNullException(nameof(metricProcedure));
17 | if (cellBuilderFunc == null) throw new ArgumentNullException(nameof(cellBuilderFunc));
18 |
19 | _metricProcedure = metricProcedure;
20 | _cellBuilderFunc = cellBuilderFunc;
21 | }
22 |
23 | protected override IMetric CreateNewMetric()
24 | {
25 | return new FuncMultiMetric(_metricProcedure, _cellBuilderFunc);
26 | }
27 |
28 | protected override void AddResult(IResult result)
29 | {
30 | _metricProcedure(_row, result);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/GlobalTimerMaxValueMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
7 | {
8 | public class GlobalTimerMaxValueMetric : IMetric
9 | {
10 | private TimeSpan _maxValue = TimeSpan.MinValue;
11 |
12 | public GlobalTimerMaxValueMetric(string name = "Timer Max")
13 | {
14 | if (name == null)
15 | throw new ArgumentNullException(nameof(name));
16 |
17 | ColumnNames = new[] { name };
18 | }
19 |
20 | public IMetric CreateNew()
21 | {
22 | return new GlobalTimerMaxValueMetric(ColumnNames[0]);
23 | }
24 |
25 | public void Add(IResult data)
26 | {
27 | if (data.IterationFinished > _maxValue)
28 | _maxValue = data.IterationFinished;
29 | }
30 |
31 | public string[] ColumnNames { get; }
32 | public object[] Values => new object[] { _maxValue };
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/GlobalTimerMinValueMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
7 | {
8 | public class GlobalTimerMinValueMetric : IMetric
9 | {
10 | private TimeSpan _minValue = TimeSpan.MaxValue;
11 |
12 | public GlobalTimerMinValueMetric(string name = "Timer Min")
13 | {
14 | if (name == null)
15 | throw new ArgumentNullException(nameof(name));
16 |
17 | ColumnNames = new[] { name };
18 | }
19 |
20 | public IMetric CreateNew()
21 | {
22 | return new GlobalTimerMinValueMetric(ColumnNames[0]);
23 | }
24 |
25 | public void Add(IResult data)
26 | {
27 | if (data.IterationStarted < _minValue)
28 | _minValue = data.IterationStarted;
29 | }
30 |
31 | public string[] ColumnNames { get; }
32 | public object[] Values => new object[] { _minValue };
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/GlobalTimerPeriodMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
4 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
6 |
7 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
8 | {
9 | public class GlobalTimerPeriodMetric : IMetric
10 | {
11 | TimeSpan _min = TimeSpan.MaxValue;
12 | TimeSpan _max = TimeSpan.MinValue;
13 |
14 | public Func Formatter = (t) => t.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
15 |
16 | public GlobalTimerPeriodMetric(string name = "Timer period (ms)")
17 | {
18 | ColumnNames = new[] { name };
19 | }
20 |
21 | public IMetric CreateNew()
22 | {
23 | return new GlobalTimerPeriodMetric(ColumnNames[0])
24 | {
25 | Formatter = Formatter
26 | };
27 | }
28 |
29 | public void Add(IResult data)
30 | {
31 | if (_min > data.IterationStarted)
32 | _min = data.IterationStarted;
33 |
34 | if (_max < data.IterationFinished)
35 | _max = data.IterationFinished;
36 | }
37 |
38 | public string[] ColumnNames { get; }
39 | public object[] Values => new [] { Formatter(_max - _min) };
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/MaxDurationMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
4 | using Viki.LoadRunner.Engine.Analytics;
5 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
6 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
7 | using Viki.LoadRunner.Engine.Core.Scenario;
8 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
9 |
10 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
11 | {
12 | public class MaxDurationMetric : IMetric
13 | {
14 | private readonly FlexiRow _row = new FlexiRow(() => long.MinValue);
15 | private readonly string[] _ignoredCheckpoints;
16 |
17 | public MaxDurationMetric(params string[] ignoredCheckpoints)
18 | {
19 | if (ignoredCheckpoints == null)
20 | throw new ArgumentNullException(nameof(ignoredCheckpoints));
21 |
22 | _ignoredCheckpoints = ignoredCheckpoints.Union(Checkpoint.NotMeassuredCheckpoints).ToArray();
23 | }
24 |
25 | public IMetric CreateNew()
26 | {
27 | return new MaxDurationMetric(_ignoredCheckpoints);
28 | }
29 |
30 | public void Add(IResult result)
31 | {
32 | ICheckpoint[] checkpoints = result.Checkpoints;
33 | for (int i = 0, j = checkpoints.Length - 1; i < j; i++)
34 | {
35 | ICheckpoint checkpoint = checkpoints[i];
36 | if (checkpoint.Error == null && _ignoredCheckpoints.All(name => name != checkpoint.Name))
37 | {
38 | string key = "Max: " + checkpoint.Name;
39 | TimeSpan momentDiff = checkpoint.Diff(checkpoints[i + 1]);
40 |
41 | if (_row[key] < momentDiff.TotalMilliseconds)
42 | _row[key] = Convert.ToInt64(momentDiff.TotalMilliseconds);
43 |
44 | }
45 | }
46 | }
47 |
48 | public string[] ColumnNames => _row.Keys.ToArray();
49 | public object[] Values => _row.Values.Select(v => (object)v).ToArray();
50 |
51 | }
52 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/MetricBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 |
6 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
7 | {
8 | public abstract class MetricBase : IMetric
9 | {
10 | protected readonly string _keyName;
11 | protected readonly TValue _initialValue;
12 | protected TValue _value;
13 |
14 | protected MetricBase(string keyName, TValue initialValue)
15 | {
16 | if (keyName == null)
17 | throw new ArgumentNullException(nameof(keyName));
18 |
19 | _keyName = keyName;
20 | _value = initialValue;
21 | _initialValue = initialValue;
22 | }
23 |
24 | protected abstract IMetric CreateNewMetric();
25 |
26 | IMetric IMetric.CreateNew()
27 | {
28 | return CreateNewMetric();
29 | }
30 |
31 | protected abstract void AddResult(IResult result);
32 |
33 | void IMetric.Add(IResult result)
34 | {
35 | AddResult(result);
36 | }
37 |
38 | string[] IMetric.ColumnNames => new[] { _keyName };
39 | object[] IMetric.Values => new[] { (object)_value };
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/MinDurationMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Scenario;
6 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
7 |
8 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
9 | {
10 | public class MinDurationMetric : MultiMetricBase
11 | {
12 | private readonly string[] _ignoredCheckpoints;
13 |
14 | public MinDurationMetric(params string[] ignoredCheckpoints)
15 | : base(() => long.MaxValue)
16 | {
17 | if (ignoredCheckpoints == null)
18 | throw new ArgumentNullException(nameof(ignoredCheckpoints));
19 |
20 | _ignoredCheckpoints = ignoredCheckpoints.Union(Checkpoint.NotMeassuredCheckpoints).ToArray();
21 | }
22 |
23 | protected override IMetric CreateNewMetric()
24 | {
25 | return new MinDurationMetric(_ignoredCheckpoints);
26 | }
27 |
28 | protected override void AddResult(IResult result)
29 | {
30 | ICheckpoint[] checkpoints = result.Checkpoints;
31 | for (int i = 0, j = checkpoints.Length - 1; i < j; i++)
32 | {
33 | ICheckpoint checkpoint = checkpoints[i];
34 | if (checkpoint.Error == null && _ignoredCheckpoints.All(name => name != checkpoint.Name))
35 | {
36 | string key = "Min: " + checkpoint.Name;
37 | TimeSpan momentDiff = checkpoint.Diff(checkpoints[i + 1]);
38 |
39 | if (_row[key] > momentDiff.TotalMilliseconds)
40 | _row[key] = Convert.ToInt64(momentDiff.TotalMilliseconds);
41 | }
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/MultiMetricBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
4 | using Viki.LoadRunner.Engine.Analytics;
5 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
6 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
7 |
8 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
9 | {
10 | public abstract class MultiMetricBase : IMetric
11 | {
12 | protected readonly FlexiRow _row;
13 |
14 | protected MultiMetricBase(Func cellBuilderFunc)
15 | {
16 | _row = new FlexiRow(cellBuilderFunc);
17 | }
18 |
19 | protected abstract IMetric CreateNewMetric();
20 |
21 | IMetric IMetric.CreateNew()
22 | {
23 | return CreateNewMetric();
24 | }
25 |
26 | protected abstract void AddResult(IResult result);
27 |
28 | void IMetric.Add(IResult result)
29 | {
30 | AddResult(result);
31 | }
32 |
33 | string[] IMetric.ColumnNames => _row.Keys.ToArray();
34 | object[] IMetric.Values => _row.Values.Select(v => (object) v).ToArray();
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Metrics/TransactionsPerSecMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Aggregators.Interfaces;
4 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
6 |
7 | namespace Viki.LoadRunner.Engine.Aggregators.Metrics
8 | {
9 | ///
10 | /// Counts successful transactions per second
11 | ///
12 | public class TransactionsPerSecMetric : IMetric
13 | {
14 | private TimeSpan _iterationStartedMin;
15 | private TimeSpan _iterationFinishedMax;
16 |
17 | private uint _count;
18 |
19 | public TransactionsPerSecMetric()
20 | {
21 | _count = 0;
22 |
23 | _iterationStartedMin = TimeSpan.MaxValue;
24 | _iterationFinishedMax = TimeSpan.MinValue;
25 | }
26 |
27 | IMetric IMetric.CreateNew()
28 | {
29 | return new TransactionsPerSecMetric();
30 | }
31 |
32 | void IMetric.Add(IResult result)
33 | {
34 | if (result.IterationStarted < _iterationStartedMin)
35 | _iterationStartedMin = result.IterationStarted;
36 |
37 | if (result.IterationFinished > _iterationFinishedMax)
38 | _iterationFinishedMax = result.IterationFinished;
39 |
40 | if (result.Checkpoints.All(c => c.Error == null))
41 | {
42 | _count++;
43 | }
44 | }
45 |
46 | private TimeSpan GetDurationDelta()
47 | {
48 | if (_count == 0)
49 | return TimeSpan.Zero;
50 |
51 | return _iterationFinishedMax - _iterationStartedMin;
52 | }
53 |
54 | string[] IMetric.ColumnNames { get; } = { "TPS" };
55 |
56 | object[] IMetric.Values
57 | {
58 | get
59 | {
60 | TimeSpan delta = GetDurationDelta();
61 |
62 | if (delta == TimeSpan.Zero)
63 | return new object[] {0.0};
64 | else
65 | return new object[] {(double)_count / delta.TotalSeconds };
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/StreamAggregatorBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
5 | using Viki.LoadRunner.Engine.Core.Collector.Pipeline;
6 | using Viki.LoadRunner.Engine.Core.Collector.Pipeline.Extensions;
7 |
8 | namespace Viki.LoadRunner.Engine.Aggregators
9 | {
10 | ///
11 | /// StreamAggregator provides loadtest raw/masterdata (IResult) IEnumerable stream
12 | ///
13 | public abstract class StreamAggregatorBase : IAggregator, IDisposable
14 | {
15 | #region Fields
16 |
17 | private BatchingPipe _queue;
18 | private Task _writerTask;
19 |
20 | #endregion
21 |
22 | #region Abstract
23 |
24 | protected abstract void Process(IEnumerable stream);
25 |
26 | #endregion
27 |
28 | #region IResultsAggregator
29 |
30 | public void Begin()
31 | {
32 | _queue = new BatchingPipe();
33 |
34 | _writerTask = _queue.ToEnumerableAsync(Process);
35 | }
36 |
37 | public void Aggregate(IResult result)
38 | {
39 | AssertTask();
40 |
41 | _queue.Produce(result);
42 | }
43 |
44 | public void End()
45 | {
46 | if (_queue != null)
47 | {
48 | _queue.ProducingCompleted();
49 | _queue = null;
50 |
51 | _writerTask.Wait();
52 |
53 | AssertTask();
54 | }
55 | }
56 |
57 | #endregion
58 |
59 | #region IDisposable
60 |
61 | ~StreamAggregatorBase()
62 | {
63 | Dispose();
64 | }
65 |
66 | public void Dispose()
67 | {
68 | ((IAggregator)this).End();
69 | }
70 |
71 | #endregion
72 |
73 | #region Misc
74 |
75 | private void AssertTask()
76 | {
77 | if (_writerTask.Exception != null)
78 | throw _writerTask.Exception;
79 | }
80 |
81 | #endregion
82 | }
83 |
84 |
85 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Utils/OrderLearner.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Viki.LoadRunner.Engine.Aggregators.Utils
4 | {
5 | ///
6 | /// Tool for learning sort order of columns
7 | ///
8 | public class OrderLearner
9 | {
10 | private List _nameOrder;
11 | public IReadOnlyList LearnedOrder => _nameOrder;
12 |
13 | public OrderLearner()
14 | {
15 | Reset();
16 | }
17 |
18 | public void Reset()
19 | {
20 |
21 | _nameOrder = new List();
22 | }
23 |
24 | public void Learn(IEnumerable names)
25 | {
26 |
27 | string previousName = "";
28 | foreach (string name in names)
29 | {
30 | if (!_nameOrder.Contains(name))
31 | {
32 | if (_nameOrder.Count == 0)
33 | {
34 | _nameOrder.Add(name);
35 | }
36 | else
37 | {
38 | int insertPosition = _nameOrder.FindIndex(s => s == previousName) + 1;
39 |
40 | if (insertPosition == 0 || insertPosition == _nameOrder.Count)
41 | _nameOrder.Add(name);
42 | else
43 | _nameOrder.Insert(insertPosition, name);
44 | }
45 | }
46 |
47 | previousName = name;
48 | }
49 |
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Utils/ReplayResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using Viki.LoadRunner.Engine.Core.Collector;
5 | using Viki.LoadRunner.Engine.Core.Scenario;
6 | using Viki.LoadRunner.Engine.Core.Scenario.Interfaces;
7 |
8 | namespace Viki.LoadRunner.Engine.Aggregators.Utils
9 | {
10 | [DebuggerDisplay("T:{ThreadIterationId} G:{GlobalIterationId} L:{ThreadIterationId} TS:{(int)(IterationStarted.TotalMilliseconds)}")]
11 | public class ReplayResult : IterationResult
12 | {
13 | private Checkpoint[] _realCheckpoints;
14 |
15 | // This helps deserializers know what implementation of Checkpoints to use.
16 | public new Checkpoint[] Checkpoints
17 | {
18 | get { return _realCheckpoints; }
19 | set
20 | {
21 | base.Checkpoints = value.Cast().ToArray();
22 | _realCheckpoints = value;
23 | }
24 | }
25 |
26 | public new TUserData UserData
27 | {
28 | get { return (TUserData)base.UserData; }
29 | set { base.UserData = value; }
30 | }
31 |
32 | ///
33 | /// offsets IterationStarted and IterationFinished values by provided offset
34 | ///
35 | ///
36 | public void Offset(TimeSpan offset)
37 | {
38 | IterationStarted += offset;
39 | IterationFinished += offset;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Utils/ResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Viki.LoadRunner.Engine.Core.Collector.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Aggregators.Utils
5 | {
6 | public static class ResultExtensions
7 | {
8 | public static void Replay(this IEnumerable results, params IAggregator[] agregators)
9 | {
10 | StreamAggregator.Replay(results, agregators);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Aggregators/Utils/UnixDateTimeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Viki.LoadRunner.Engine.Aggregators.Utils
4 | {
5 | public static class UnixDateTimeExtensions
6 | {
7 | public static DateTime UnixTimeStart = new DateTime(1970, 1, 1, 0, 0, 0, 0);
8 |
9 | public static long ToUnixTimeMs(this DateTime currentDateTime)
10 | {
11 | return (long)(currentDateTime - UnixTimeStart).TotalMilliseconds;
12 | }
13 |
14 | public static int ToUnixTime(this DateTime currentDateTime)
15 | {
16 | return (int)(currentDateTime - UnixTimeStart).TotalSeconds;
17 | }
18 |
19 | public static DateTime UnixTimeToDateTime(int unixTime)
20 | {
21 | return UnixTimeStart.AddSeconds(unixTime);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/DimensionKey.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable 1591
2 |
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | namespace Viki.LoadRunner.Engine.Analytics
7 | {
8 | public class DimensionKey
9 | {
10 | private readonly string[] _dimensionValues;
11 | private readonly string _cachedKey;
12 |
13 | public IReadOnlyList Values => _dimensionValues;
14 |
15 | public DimensionKey(string[] dimensionValues)
16 | {
17 | if (dimensionValues == null)
18 | throw new ArgumentNullException(nameof(dimensionValues));
19 |
20 | _dimensionValues = dimensionValues;
21 | _cachedKey = String.Join(" ", dimensionValues);
22 | }
23 |
24 |
25 | public override bool Equals(object obj)
26 | {
27 | return (obj as DimensionKey)?._cachedKey == _cachedKey;
28 | }
29 |
30 | public override int GetHashCode()
31 | {
32 | return _cachedKey.GetHashCode();
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Dimensions/FuncDimension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Analytics.Dimensions
5 | {
6 | public class FuncDimension : IDimension
7 | {
8 | private readonly Func _dimensionValueSelector;
9 |
10 | /// Name/Key of custom dimension
11 | /// Dimension value selector
12 | public FuncDimension(string dimensionName, Func dimensionValueSelector)
13 | {
14 | if (dimensionName == null)
15 | throw new ArgumentNullException(nameof(dimensionName));
16 | if (dimensionValueSelector == null)
17 | throw new ArgumentNullException(nameof(dimensionValueSelector));
18 |
19 | _dimensionValueSelector = dimensionValueSelector;
20 | DimensionName = dimensionName;
21 | }
22 |
23 | public string DimensionName { get; }
24 |
25 | string IDimension.GetKey(T result)
26 | {
27 | return _dimensionValueSelector(result);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Dimensions/ThresholdDimension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 |
5 | namespace Viki.LoadRunner.Engine.Analytics.Dimensions
6 | {
7 | public class ThresholdDimension : IDimension
8 | where TDuration : IComparable
9 | {
10 | private readonly ValueSelector _selector;
11 |
12 | private readonly TDuration[] _thresholds;
13 | private readonly string[] _thresholdStrings;
14 |
15 | public string DefaultOutOfRange = "OutOfRange";
16 |
17 | public ThresholdDimension(ValueSelector selector, TDuration[] thresholds)
18 | : this(selector, value => value.ToString(), thresholds)
19 | {
20 | }
21 |
22 | public ThresholdDimension(ValueSelector selector, DimensionFormatter formatter, params TDuration[] thresholds)
23 | {
24 | if (formatter == null) throw new ArgumentNullException(nameof(formatter));
25 | _selector = selector ?? throw new ArgumentNullException(nameof(selector));
26 | _thresholds = thresholds ?? throw new ArgumentNullException(nameof(thresholds));
27 |
28 | Array.Sort(_thresholds);
29 | _thresholdStrings = _thresholds.Select(t => formatter(t)).ToArray();
30 | }
31 |
32 | public string GetKey(TData data)
33 | {
34 | TDuration value = _selector(data);
35 |
36 | for (int i = 0; i < _thresholds.Length; i++)
37 | {
38 | if (value.CompareTo(_thresholds[i]) == -1)
39 | return _thresholdStrings[i];
40 | }
41 |
42 | return DefaultOutOfRange;
43 | }
44 |
45 | public string DimensionName { get; set; } = "Threshold";
46 |
47 | public delegate TDuration ValueSelector(TData data);
48 | public delegate string DimensionFormatter(TDuration value);
49 | }
50 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Dimensions/TimeDimension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Analytics.Dimensions
5 | {
6 | ///
7 | /// Split results in provided time intervals
8 | ///
9 | public class TimeDimension : IDimension
10 | {
11 | public readonly TimeSpan Interval;
12 |
13 | private readonly TimeSelectorDelegate _timeSelector;
14 |
15 | public Func Formatter = t => ((long)t.TotalSeconds).ToString();
16 |
17 | /// interval timespan
18 | /// TimeSpan selector for which dimension key will be calculated
19 | /// Custom name for dimension
20 | public TimeDimension(TimeSpan interval, TimeSelectorDelegate timeSelector, string dimensionName = "Time (s)")
21 | {
22 | Interval = interval;
23 | _timeSelector = timeSelector ?? throw new ArgumentNullException(nameof(timeSelector));
24 | DimensionName = dimensionName;
25 | }
26 |
27 | public string DimensionName { get; }
28 |
29 | string IDimension.GetKey(T data)
30 | {
31 | TimeSpan resultTimeSlot = Calculate(Interval, _timeSelector(data));
32 |
33 | return Formatter(resultTimeSlot);
34 | }
35 |
36 | ///
37 | /// Calculates TimeSpan value for dimension key.
38 | ///
39 | public static TimeSpan Calculate(TimeSpan interval, TimeSpan time)
40 | {
41 | return TimeSpan.FromTicks(((int)(time.Ticks / interval.Ticks)) * interval.Ticks);
42 | }
43 |
44 | public delegate TimeSpan TimeSelectorDelegate(TData data);
45 | }
46 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/DimensionsHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
4 |
5 | namespace Viki.LoadRunner.Engine.Analytics
6 | {
7 | public class DimensionsHandler
8 | {
9 | private readonly IDimension[] _dimensions;
10 |
11 | public DimensionsHandler(IEnumerable> dimensions)
12 | {
13 | _dimensions = dimensions.ToArray();
14 | }
15 |
16 | public DimensionKey GetValue(T result)
17 | {
18 | string[] dimensionValues = _dimensions.Select(d => d.GetKey(result)).ToArray();
19 |
20 | DimensionKey resultKey = new DimensionKey(dimensionValues);
21 |
22 | return resultKey;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Extensions/HistogramExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
3 |
4 | namespace Viki.LoadRunner.Engine.Analytics.Extensions
5 | {
6 | public static class HistogramExtensions
7 | {
8 | ///
9 | /// Copies histogram setup from source to target, overwriting all preexisting configuration in target
10 | ///
11 | public static void CopySettings(this IHistogramBuilder source, IHistogramBuilder target)
12 | {
13 | target.Dimensions.Clear();
14 | target.Dimensions.AddRange(source.Dimensions);
15 |
16 | target.Metrics.Clear();
17 | target.Metrics.AddRange(source.Metrics);
18 |
19 | target.ColumnAliases.Clear();
20 | foreach (KeyValuePair item in source.ColumnAliases)
21 | {
22 | target.ColumnAliases.Add(item.Key, item.Value);
23 | }
24 |
25 | target.ColumnIgnoreNames.Clear();
26 | target.ColumnIgnoreNames.AddRange(source.ColumnIgnoreNames);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/FlexiRow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace Viki.LoadRunner.Engine.Analytics
6 | {
7 | public class FlexiRow : IEnumerable>
8 | {
9 | #region Fields
10 |
11 | private readonly Func _valueBuilderFunc;
12 | private readonly Dictionary _row = new Dictionary();
13 |
14 | #endregion
15 |
16 | #region Constructor
17 |
18 | public FlexiRow(Func valueBuilderFunc)
19 | {
20 | if (valueBuilderFunc == null)
21 | throw new ArgumentNullException(nameof(valueBuilderFunc));
22 |
23 | _valueBuilderFunc = valueBuilderFunc;
24 | }
25 |
26 | #endregion
27 |
28 | #region public functionality
29 |
30 | public void Touch(TKey key)
31 | {
32 | if (!_row.ContainsKey(key))
33 | _row.Add(key, _valueBuilderFunc());
34 | }
35 |
36 | public TValue this[TKey key]
37 | {
38 | get
39 | {
40 | TValue value;
41 | if (!_row.TryGetValue(key, out value))
42 | {
43 | value = _valueBuilderFunc();
44 | _row.Add(key, value);
45 | }
46 |
47 | return value;
48 | }
49 | set { _row[key] = value; }
50 | }
51 |
52 | public int Count => _row.Count;
53 |
54 | public void Clear()
55 | {
56 | _row.Clear();
57 | }
58 |
59 | public IEnumerable Keys => _row.Keys;
60 | public IEnumerable Values => _row.Values;
61 |
62 | #endregion
63 |
64 | #region IEnumerable
65 |
66 | IEnumerator> IEnumerable>.GetEnumerator()
67 | {
68 | return _row.GetEnumerator();
69 | }
70 |
71 | IEnumerator IEnumerable.GetEnumerator()
72 | {
73 | return ((IEnumerable>)this).GetEnumerator();
74 | }
75 |
76 | #endregion
77 | }
78 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Interfaces/IDimension.cs:
--------------------------------------------------------------------------------
1 | namespace Viki.LoadRunner.Engine.Analytics.Interfaces
2 | {
3 | public interface IDimension
4 | {
5 | ///
6 | /// DisplayName/Key of the column
7 | ///
8 | string DimensionName { get; }
9 |
10 | ///
11 | /// Build dimension key by provided row of raw data
12 | ///
13 | /// row of raw data
14 | /// String dimension key
15 | string GetKey(T data);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Interfaces/IHistogramBuilder.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Viki.LoadRunner.Engine.Analytics.Interfaces
4 | {
5 | public interface IHistogramBuilder
6 | {
7 | List> Dimensions { get; }
8 | List> Metrics { get; }
9 |
10 | Dictionary ColumnAliases { get; }
11 | List ColumnIgnoreNames { get; }
12 |
13 | Dictionary MetricsPostProcess { get; }
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Interfaces/IMetric.cs:
--------------------------------------------------------------------------------
1 | namespace Viki.LoadRunner.Engine.Analytics.Interfaces
2 | {
3 | public interface IMetric
4 | {
5 | ///
6 | /// Create new blank IMetric instance based on current instance settings (e.g. settings passed in the constructor in histogram setup)
7 | ///
8 | ///
9 | IMetric CreateNew();
10 |
11 | ///
12 | /// Aggregate row of raw data
13 | ///
14 | /// row of raw data
15 | void Add(T data);
16 |
17 | ///
18 | /// Names of columns produced by this metric (order must match [Values] order)
19 | ///
20 | string[] ColumnNames { get; }
21 | ///
22 | /// Values produced by this metric (order must match [ColumnNames] order)
23 | ///
24 | object[] Values { get; }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Viki.LoadRunner.Engine/Analytics/Metrics/AverageMetric.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Viki.LoadRunner.Engine.Analytics.Interfaces;
3 | using Viki.LoadRunner.Engine.Analytics.Metrics.Calculators;
4 |
5 | namespace Viki.LoadRunner.Engine.Analytics.Metrics
6 | {
7 | public class AverageMetric : IMetric
8 | {
9 | private readonly string _name;
10 | private readonly DoubleSelectorDelegate _selector;
11 |
12 | private readonly AverageCalculator _calculator = new AverageCalculator();
13 |
14 | public AverageMetric(DoubleSelectorDelegate selector)
15 | : this("Average", selector)
16 | {
17 | }
18 |
19 | public AverageMetric(string name, DoubleSelectorDelegate selector)
20 | {
21 | _name = name;
22 | _selector = selector;
23 | }
24 |
25 | IMetric IMetric.CreateNew()
26 | {
27 | return new AverageMetric(_name, _selector);
28 | }
29 |
30 | void IMetric.Add(T result)
31 | {
32 | _calculator.Add(_selector(result));
33 | }
34 |
35 | string[] IMetric.ColumnNames => _calculator.SampleCount > 0 ? new[] { _name } : Array.Empty();
36 |
37 | object[] IMetric.Values => _calculator.SampleCount > 0
38 | ? new object[] { _calculator.GetAverage() }
39 | : Array.Empty