public static IObservable <StartUnitTest> GetTargetsFromBfc(string bfcFile, IServiceCommandFactory parseTestSyntax, IObservable <UnitTestResult> forParallelExection) { var pattern = bfcFile.Split('/', '\\'); var dir = pattern.Take(pattern.Length - 1).ToStringEach("/"); var filePattern = pattern.Last(); var watchers = new CompositeDisposable(); return(Rxn.Create <StartUnitTest>(o => { Files.WatchForChanges(dir, filePattern, true, false, false).SelectMany(_ => TestWorkflow.StartIntegrationTest(File.ReadAllText(bfcFile), parseTestSyntax, forParallelExection).Do(dll => o.OnNext(dll))) .Until().DisposedBy(watchers); if (bfcFile.EndsWith(".bfc", StringComparison.InvariantCultureIgnoreCase)) { if (!File.Exists(bfcFile)) { $"Could not find theBFG command file: {bfcFile}".LogDebug(); o.OnCompleted(); } TestWorkflow.StartIntegrationTest(File.ReadAllText(bfcFile), parseTestSyntax, forParallelExection).Do(dll => o.OnNext(dll)).Until(); } return watchers; }) ); }
private static IObservable <Unit> SelfDestructIf(string[] args) { return(Rxn.Create(() => { var destruct = args.Skip(1).FirstOrDefault().IsNullOrWhiteSpace("fail"); var quiteMode = args.Last().IsNullOrWhiteSpace("nup"); if (!destruct.BasicallyEquals("destruct")) { return; } if (!quiteMode.BasicallyEquals("quite")) { "Please type 'y' to delete ALL BFG DATA under this root".LogDebug(); if (Console.Read() != 'y') { "Aborting".LogDebug(); return; } } Directory.Delete(DataDir, true); "RESET !!!".LogDebug(); }) .FinallyR(() => { theBfg.IsCompleted.OnNext(new Unit()); theBfg.IsCompleted.OnCompleted(); })); }
//todo: write unit test for this public static IObservable <UnitTestDiscovered> DiscoverUnitTests(string testDllSelector, string[] args, Func <ITestArena[]> arenas) { return(Rxn.Create <UnitTestDiscovered>(o => { return GetTargets(testDllSelector, args, arenas, null, null) .Do(t => { ListTests(t.Dll, arenas) .Select( tests => { return new UnitTestDiscovered() { Dll = t.Dll, DiscoveredTests = tests.ToArray() }; }) .Do(o.OnNext) .Until(); }) .Subscribe(); //todo fix hanging }) .Publish() .RefCount()); }
public static IObservable <Unit> ReloadWithTestWorker(params string[] args) { return(Rxn.Create <Unit>(o => { var apiName = args.Skip(1).FirstOrDefault(); var testHostUrl = GetTestarenaUrlFromArgs(args); theBfg.Args = args; RxnExtensions.DeserialiseImpl = (t, json) => JsonExtensions.FromJson(json, t); RxnExtensions.SerialiseImpl = (json) => JsonExtensions.ToJson(json); var cfg = RxnAppCfg.Detect(args); //var appStore = new CurrentDirectoryAppUpdateStore(); //var clusterHost = OutOfProcessFactory.CreateClusterHost(args, appStore, cfg); return theBfgDef.TestWorker(apiName, testHostUrl, RxnApp.SpareReator(testHostUrl)).ToRxns() .Named(new bfgAppInfo("bfgWorker")) .OnHost(new ConsoleHostedApp(), cfg) .SelectMany(h => h.Run().Do(app => { theBfg.IsReady.OnNext(app); })) .Select(app => { return new Unit(); }) .Subscribe(o); })); }
public IObservable <CommandResult> Handle(TagWorker command) { return(Rxn.Create(() => { Info[bfgTagWorkflow.WorkerTag] = $"{Info[bfgTagWorkflow.WorkerTag]},{command.Tags.ToStringEach()}"; return CommandResult.Success().AsResultOf(command).ToObservable(); })); }
public IObservable <CommandResult> Handle(UntagWorker command) { return(Rxn.Create(() => { Info[bfgTagWorkflow.WorkerTag] = Info[bfgTagWorkflow.WorkerTag].Split(',').Except(command.Tags).ToStringEach(); return CommandResult.Success().AsResultOf(command).ToObservable(); })); }
public IObservable <CommandResult> Handle(Reload command) { return(Rxn.Create(() => { "Reloading".LogDebug(); Fire(); return CommandResult.Success().AsResultOf(command); })); }
private static IObservable <string> GetOrCreateWatcher(string testDll) { var dlld = new FileInfo(testDll); if (_watchers.ContainsKey(dlld.FullName)) { return(Rxn.Empty <string>()); //already subscribed to updates } var watcher = Files.WatchForChanges(dlld.DirectoryName, dlld.Name, true, false, false); _watchers.Add(dlld.FullName, watcher); return(watcher); }
public static IObservable <Unit> ReloadAnd(string url = "http://*****:*****@test.dll and fire".LogDebug(); "fire".LogDebug(); "fire @url".LogDebug(); "fire rapidly {{threadCount | max}} | will fire on multiple threads simultatiously".LogDebug(); "fire compete | shard test-suite execution across multiple nodes".LogDebug(); "".LogDebug(); "launch [email protected] | deploy apps to worker nodes automatically during CI/CD. Worker integration via complementary C# api: theBfgApi.launch(\"app\", \"dir\")".LogDebug(); "".LogDebug(); "".LogDebug(); "<<USE WITH CAUTION>>".LogDebug(); "".LogDebug(); break; } return Disposable.Empty; })); }
public static IObservable <Unit> ReloadWithTestArena(params string[] args) { return(Rxn.Create <Unit>(o => { ReportStatus.StartupLogger = ReportStatus.Log.ReportToConsole(); theBfg.Args = args; "Configuring App".LogDebug(); theBFGAspNetCoreAdapter.Appcfg = RxnAppCfg.Detect(args); return AspNetCoreWebApiAdapter.StartWebServices <theBFGAspNetCoreAdapter>(theBFGAspNetCoreAdapter.Cfg, args).ToObservable() .LastAsync() .Select(_ => new Unit()) .Subscribe(o); })); }
public IObservable <IRxn> Start(string name, StartUnitTest work, StreamWriter testLog, string logDir) { var testEventStream = new Subject <IRxn>(); //https://github.com/dotnet/sdk/issues/5514 var dotnetHack = PathToTestArenaProcess(); var logName = $"{name}{work.RunThisTest.Substring(0, work.RunThisTest.Length > 25 ? 25 : work.RunThisTest.Length).IsNullOrWhiteSpace(new FileInfo(work.Dll).Name)}".LogDebug("Targeting"); bool isreadingOutputMessage = false; var lastLine = false; OnStart(work); return(Rxn.Create ( dotnetHack, StartTestsCmd(work, logDir), i => { if (i == null) { return; } foreach (var progress in OnLog(name, work, i)) { testEventStream.OnNext(progress); } if (testLog.BaseStream.CanWrite) { testLog.WriteLine(i.LogDebug(logName)); } }, e => testLog.WriteLine(e.LogDebug(logName)) ) .FinallyR(() => { foreach (var e in OnEnd(work.Dll)) { testEventStream.OnNext(e); } testEventStream.OnCompleted(); }) .SelectMany(_ => testEventStream) ); }
} //Hmm this kind of state is bad. i should ditch static on everything? private static IObservable <StartUnitTest> GetTargetsFromPath(string[] args, string testSyntax, Func <ITestArena[]> arena) { var pattern = testSyntax.Split('/', '\\'); var dir = pattern.Take(pattern.Length - 1).ToStringEach("/"); var filePattern = pattern.Last(); var watchers = new CompositeDisposable(); return(Rxn.Create <StartUnitTest>(o => { foreach (var dll in Directory.GetFileSystemEntries(dir, filePattern, SearchOption.AllDirectories).Select(d => d.AsCrossPlatformPath()).Where(NotAFrameworkFile)) { GetTargetsFromDll(args, dll, arena).Do(t => o.OnNext(t)).Until(); } return watchers; })); }
public IObservable <CommandResult> Handle(StopUnitTest command) { return(Rxn.Create(() => { "Stopping all unit tests".LogDebug(); foreach (var worker in _fanoutStrategy.Workers.Values) { try { worker.DoWork?.Dispose(); } catch (Exception e) { $"Failed to stop {worker.Worker.Name} : {e}".LogDebug(); } } return CommandResult.Success().AsResultOf(command); })); }
public static IObservable <StartUnitTest> GetTargets(string testSyntax, string[] args, Func <ITestArena[]> forCompete, IServiceCommandFactory parseTestSyntax, IObservable <UnitTestResult> forParallelExection) { return(testSyntax.IsNullOrWhitespace() ? Rxn.Empty <StartUnitTest>() : Rxn.DfrCreate <StartUnitTest>(() => { if (!(testSyntax.Contains(".dll", StringComparison.InvariantCultureIgnoreCase) || testSyntax.Contains(".csproj", StringComparison.InvariantCultureIgnoreCase) || testSyntax.Contains(".bfc", StringComparison.InvariantCultureIgnoreCase))) { "Target must be either .dll or .csproj or .bfc".LogDebug(); return Rxn.Empty <StartUnitTest>(); } if (testSyntax.Contains(".bfc")) { return GetTargetsFromBfc(testSyntax, parseTestSyntax, forParallelExection); } if (!testSyntax.Contains("*")) { return GetTargetsFromDll(args, testSyntax, forCompete); } else { return GetTargetsFromPath(args, testSyntax, forCompete); } }) .Select(e => { e.Dll = e.Dll.AsCrossPlatformPath(); if (!FocusedTest.IsNullOrWhitespace()) { e.RunThisTest = FocusedTest; } return e; })); }
public IObservable <IEnumerable <string> > ListTests(string dll) { var testArenaProcess = PathToTestArenaProcess(); var tests = new List <string>(); return(Rxn.Create ( testArenaProcess, ListTestsCmd(dll), i => { i.LogDebug(); foreach (var test in OnTestCmdLog(i)) { tests.Add(test); } }, e => $"failed to parse test: {e}".LogDebug() ) .Aggregate(tests.ToObservable(), (a, b) => a) .SelectMany(e => e) ); }
public IObservable <CommandResult> Handle(StartUnitTest command) { Queue(command); //the result will be broadcast when the queue processes the command return(Rxn.Empty <CommandResult>()); }
public IObservable <UnitTestResult> DoWork(StartUnitTest work) { work.Dll = work.Dll.EnsureRooted(); var logId = $"{Name.Replace("#", "")}_{++_runId}_{DateTime.Now:dd-MM-yy-hhmmssfff}"; var logDir = Path.Combine(_cfg.AppRoot, "logs", logId).AsCrossPlatformPath(); StreamWriter testLog = null; FileStream logFile = null; return(Rxn.Create(() => //setup dirs for test { if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } logFile = File.Create(Path.Combine(logDir, "testArena.log")); testLog = new StreamWriter(logFile, leaveOpen: true); }) .SelectMany(_ => //keep the test updated while we are running it { _isBusy.OnNext(true); return RunTestSuiteInTestArena(work, testLog, logDir); }) .LastOrDefaultAsync() .Delay(TimeSpan.FromSeconds(1))//test is finished, wait for log file to unlock .SelectMany(_ => { try { testLog?.Dispose(); logFile?.Dispose(); } catch (Exception e) { $"While closing log {e}".LogDebug(); } //send the log to the return ShipLogForTest(logDir, logId, work.Id); //send logs to testArena }) .Select(_ => //return result of process { return (UnitTestResult) new UnitTestResult() { WasSuccessful = true }.AsResultOf(work); }) .Catch <UnitTestResult, Exception>(e => { return ((UnitTestResult) new UnitTestResult() { WasSuccessful = false }.AsResultOf(work)).ToObservable(); }) .FinallyR(() => { _isBusy.OnNext(false); })); }
/// <summary> /// todo: fix issue with not broadcasting all test stats to appstatus, only first time /// </summary> /// <param name="testCluster"></param> /// <param name="unitTestToRun"></param> /// <param name="publusher"></param> /// <param name="reactorMgr"></param> private void BroadcasteStatsToTestArena(IManageReactors reactorMgr) { var bfgReactor = reactorMgr.GetOrCreate("bfg").Reactor; //need to fix health which will allow this to be viewed on the appstatus portal. should monitor health of fanout stratergy // //IMonitorActionFactory<IRxn> health =MonitorHealth RxnCreator.MonitorHealth <IRxn>(bfgReactor, "theBFG", out var _before, () => Rxn.Empty()).SelectMany(bfgReactor.Output).Until(); $"Heartbeating".LogDebug(); bfgReactor.Output.Publish(new PerformAPing()).Until(); }
public IObservable <CommandResult> Handle(Run command) { var tokens = command.Cmd.Split(' '); var cmd = tokens[0]; var worker = Name; _rxnManager.Publish(new UnitTestsStarted() { TestId = command.Id, At = DateTime.Now, Tests = new[] { cmd }, Worker = "", //_appInfo.Name, WorkerId = worker }).Until(); var unitTestId = Guid.NewGuid().ToString(); _rxnManager.Publish(new UnitTestPartialResult(command.Id, "passed", cmd, "0", worker) { UnitTestId = unitTestId }).Until(); return(Rxn.Create ( RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd" : "/bin/bash", $"{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "/c" : "-c")} {command.Cmd}", i => { if (i == null) { return; } _rxnManager.Publish(new UnitTestPartialLogResult { LogMessage = i.ToString(), TestId = command.Id, UnitTestId = unitTestId, Worker = worker }).Until(); }, e => { _rxnManager.Publish(new UnitTestPartialLogResult { LogMessage = e.ToString(), TestId = command.Id, UnitTestId = unitTestId, Worker = worker }).Until(); } ) .FinallyR(() => { _rxnManager.Publish(new UnitTestOutcome() { Passed = 1, Failed = 0, InResponseTo = command.Id, UnitTestId = unitTestId, Dll = cmd }).Until(); }) .Select(_ => CommandResult.Success())); }
public IObservable <Unit> RunTestSuiteInTestArena(StartUnitTest work, StreamWriter testLog, string logDir) { $"Preparing to run {(work.RunAllTest ? "All" : work.RunThisTest)} in {work.Dll}".LogDebug(); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } var keepTestUpdatedIfRequested = work.UseAppUpdate.ToObservable(); //if not using updates, the dest folder is our root if (!File.Exists(work.Dll)) { if (work.UseAppUpdate.IsNullOrWhitespace()) { _rxnManager.Publish(new UnitTestResult() { WasSuccessful = false, Message = $"Cannot find target @{work.Dll}".LogDebug() }.AsResultOf(work)); return(Rxn.Empty <Unit>()); } keepTestUpdatedIfRequested = _updateService.KeepUpdated(work.UseAppUpdate, work.UseAppVersion, theBfg.GetTestSuiteDir(work.UseAppUpdate, work.UseAppVersion), new RxnAppCfg() { AppStatusUrl = work.AppStatusUrl.IsNullOrWhiteSpace("http://localhost:888"), SystemName = work.UseAppUpdate, KeepUpdated = true }, true); } else { work.Dll = FindIfNotExists(work.Dll); } return(keepTestUpdatedIfRequested //run the test .Select(testPath => { $"Running {work.Dll}".LogDebug(); foreach (var arena in _arena()) { try { var tests = arena.ListTests(work.Dll).WaitR(); if (tests.AnyItems()) { return arena.Start(Name, work, testLog, logDir).SelectMany(_ => _rxnManager.Publish(_)); } } catch (Exception) { continue; } } "Argh, couldnt find a test arena to run this test".LogDebug(); _rxnManager.Publish(new UnitTestResult() { WasSuccessful = false, Message = $"No test arena on host is compatible with {work.Dll}" }.AsResultOf(work)); return Rxn.Empty <Unit>(); }) .Switch()); }
public IObservable <CommandResult> Handle(StopRecording command) { return(Rxn.Create(() => _isStarted.OnNext(false)).ToObservable().Select(_ => CommandResult.Success($"Recording stopped").AsResultOf(command))); }
public static IObservable <StartUnitTest> StartIntegrationTest(string testExpression, IServiceCommandFactory cmds, IObservable <UnitTestResult> results) { var serial = testExpression.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).Where(t => !t.IsNullOrWhitespace()).ToArray(); var stageNo = 0; return(Rxn.Create <StartUnitTest>(o => { var resources = new CompositeDisposable(); return serial .SelectMany(cmd => { var stageTrigger = new Subject <Unit>(); var parallel = cmd.Split(',', StringSplitOptions.RemoveEmptyEntries) .SelectMany(t => { var testSytax = t.Split(';', StringSplitOptions.RemoveEmptyEntries); return Enumerable.Range(0, Int32.Parse(testSytax.Skip(1).FirstOrDefault().IsNullOrWhiteSpace("1"), NumberStyles.Any)).Select(_ => testSytax.Length > 0 ? testSytax[0].Trim() : string.Empty); }) .Select(action => { var tokens = action.Split(' ', StringSplitOptions.RemoveEmptyEntries); return cmds.Get(tokens.First(), tokens.Skip(1)); }) .ToArray(); if (parallel.Length < 1) { return new Unit().ToObservable(); } $"Starting stage {++stageNo} with {parallel.Length} tests in parallel".LogDebug(); var expectedResultIds = parallel.Select(t => t.Id).ToList(); results.Synchronize().Where(tr => { if (expectedResultIds.Contains(tr.InResponseTo)) { expectedResultIds.Remove(tr.InResponseTo); } if (expectedResultIds.Count < 1) { $"Stage {stageNo} complete".LogDebug(); stageTrigger.OnNext(new Unit()); stageTrigger.OnCompleted(); return true; } return false; }) .FirstOrDefaultAsync() .Until() .DisposedBy(resources); parallel.ForEach(t => o.OnNext((StartUnitTest)t)); return stageTrigger; }) .Where(_ => stageNo >= serial.Length) .FirstOrDefaultAsync() .Do(_ => o.OnCompleted()) .FinallyR(() => resources.Dispose()) .Until() ; })); }
public IObservable <IRxn> Process(TestArenaCfgUpdated @event) { @event.Cfg?.Save(); return(Rxn.Empty <IRxn>()); }