public void TimeSpanDateTime() { var simple = FSBuilder.Take <TimeSpanObject>("|", typeof(TimeSpanObject).GetProperty("A")).TakeRest(typeof(TimeSpanObject).GetField("B")).Seal(); var span1 = System.TimeSpan.FromDays(1); var span2 = System.TimeSpan.FromMilliseconds((new Random()).Next(1000000)); var obj = new TimeSpanObject(); simple(span1 + "|" + span2, obj); Assert.AreEqual(span1, obj.A); Assert.AreEqual(span2, obj.B.Value); var complex = FSBuilder.Take <TimeSpanObject>("|", typeof(TimeSpanObject).GetProperty("A"), format: "G").TakeRest(typeof(TimeSpanObject).GetField("B"), format: "g").Seal(); complex(span2.ToString("G") + "|" + span1.ToString("g"), obj); Assert.AreEqual(span2, obj.A); Assert.AreEqual(span1, obj.B.Value); var date = FSBuilder.Take <TimeSpanObject>("|", "C").Seal(); var newDate = DateTime.UtcNow; date(newDate + "|", obj); Assert.AreEqual(newDate.ToString(), obj.C.ToString()); }
public Task Clean() { FSBuilder builder = new FSBuilder(Environment.CurrentDirectory); builder.EnsureDirectoryExists("temp", false); return(Task.CompletedTask); }
public BlogBuilder(BlogOptions options, string rootPath) { RootPath = rootPath; Options = options; FsBuilder = new FSBuilder(rootPath); }
public void NumberAsEnum() { var simple = FSBuilder.Take <EnumObject>("|", "A").Else((s, o) => { throw new InvalidOperationException(); }).Seal(); var obj = new EnumObject(); simple("2|", obj); Assert.AreEqual(EnumObject.Blah.Bar, obj.A); }
public void StandAloneTakeN() { var parser = FSBuilder.Take <DecimalObject>(4, typeof(DecimalObject).GetProperty("A")).Seal(); var obj = new DecimalObject(); parser("12345678", obj); Assert.AreEqual(1234m, obj.A); }
public void Overflows() { var parse = FSBuilder .Take <ValueObject>(",", "A") .Take(",", "B") .Take(",", "C") .TakeRest("D") .Else((s, o) => { throw new Exception(); }) .Seal(); var obj = new ValueObject(); parse("1,2,3,4", obj); Assert.AreEqual(1, obj.A); Assert.AreEqual(2, obj.B); Assert.AreEqual(3, obj.C); Assert.AreEqual(4, obj.D); try { parse(byte.MaxValue + ",1,2,3", obj); Assert.Fail("byte should be exceeded"); } catch (Exception) { } try { parse("1," + ushort.MaxValue + ",2,3", obj); Assert.Fail("short should be exceeded"); } catch (Exception) { } try { parse("1,2," + uint.MaxValue + ",3", obj); Assert.Fail("int should be exceeded"); } catch (Exception) { } try { parse("1,2,3," + ulong.MaxValue, obj); Assert.Fail("long should be exceeded"); } catch (Exception) { } parse(sbyte.MaxValue + "," + short.MaxValue + "," + int.MaxValue + "," + long.MaxValue, obj); Assert.AreEqual(sbyte.MaxValue, obj.A); Assert.AreEqual(short.MaxValue, obj.B); Assert.AreEqual(int.MaxValue, obj.C); Assert.AreEqual(long.MaxValue, obj.D); }
public void Decimals() { var parser = FSBuilder .Take <DecimalObject>(",", typeof(DecimalObject).GetProperty("A")) .TakeRest(typeof(DecimalObject).GetProperty("B")) .Seal(); var obj = new DecimalObject(); parser("123.45,8675309", obj); Assert.AreEqual(123.45m, obj.A); Assert.AreEqual(8675309m, obj.B); }
public void FloatsDoubles() { var parse = FSBuilder.Take <FloatAndDouble>(",", "A").TakeRest("B").Seal(); var rand = new Random(); var a = (float)rand.NextDouble(); var b = rand.NextDouble(); var obj = new FloatAndDouble(); parse(a + "," + b, obj); Assert.AreEqual(a.ToString(), obj.A.ToString()); Assert.AreEqual(b.ToString(), obj.B.ToString()); }
public void Bools() { var parser = FSBuilder .Take <BoolObject>(",", "A") .Take(",", "B") .TakeRest("C") .Seal(); var obj = new BoolObject(); parser("True,1,false", obj); Assert.IsTrue(obj.A); Assert.IsTrue(obj.B); Assert.IsFalse(obj.C.Value); }
public void BackUntil() { var parse = FSBuilder .Skip <StringObject>(6) .Take(1, "Raw1") .Back("hello") .TakeRest("Raw2") .Seal(); var obj = new StringObject(); parse("123helloworld", obj); Assert.AreEqual("l", obj.Raw1); Assert.AreEqual("world", obj.Raw2); }
public void NullableSkip() { var parser = FSBuilder .Take <BoolObject>(",", "A") .Take(",", "C") .TakeRest("B") .Seal(); var obj = new BoolObject(); parser("True,,false", obj); Assert.IsTrue(obj.A); Assert.IsFalse(obj.B); Assert.IsNull(obj.C); }
public void FixedSteps() { var parse = FSBuilder .Take <UnsignedObject>(4, "A") .Back(2) .Take(",", "B") .Skip(2) .TakeRest("C") .Seal(); var obj = new UnsignedObject(); parse("1234,5678", obj); Assert.AreEqual((ulong)1234, obj.A); Assert.AreEqual((uint)34, obj.B); Assert.AreEqual((ushort)78, obj.C); }
public void Unsigned() { var parse = FSBuilder .Take <UnsignedObject>(",", "A") .Take(",", "B") .Take(",", "C") .TakeRest("D") .Seal(); var obj = new UnsignedObject(); parse(ulong.MaxValue + "," + uint.MaxValue + "," + ushort.MaxValue + "," + byte.MaxValue, obj); Assert.AreEqual(ulong.MaxValue, obj.A); Assert.AreEqual(uint.MaxValue, obj.B); Assert.AreEqual(ushort.MaxValue, obj.C); Assert.AreEqual(byte.MaxValue, obj.D); }
public void Guids() { var parser = FSBuilder .Take <GuidObj>(",", "A") .TakeRest("B", "X") .Else((s, o) => { throw new Exception(); }) .Seal(); var a = Guid.NewGuid(); var b = Guid.NewGuid(); var obj = new GuidObj(); parser(a + "," + b.ToString("X"), obj); Assert.AreEqual(obj.A, a); Assert.AreEqual(obj.B.Value, b); }
// TODO: fix rewrite behavior public async Task Initialize() { Option = new WorkspaceOption(); DB = new DB(); await Save(); FSBuilder builder = new FSBuilder(Environment.CurrentDirectory); builder.EnsureDirectoryExists("posts"); { using var st = builder.GetFileRewriteStream(BlogOptionPath); await JsonSerializer.SerializeAsync(st, new BlogOptions(), options : new JsonSerializerOptions { WriteIndented = true }); } await Clean(); }
public void SimpleStringPerf() { // THIS TEST IS EXPECTED TO FAIL // String performance in the hand written case is *very good*, if you // can gin up better code for this case in the FSP, submit a patch. // It's an interesting project. var regex = new Regex(@"([^,]*?),([^,]*?),([^,]*?),([^,]*)", RegexOptions.Compiled); var parserTask = FSBuilder .Take <StringPerfObject>(",", "A") .Take(",", "B") .Take(",", "C") .TakeRest("D") .Seal(); Action ghettoGC = () => { GC.Collect(); Thread.Sleep(100); }; Action <string, StringPerfObject> regexTask = delegate(string str, StringPerfObject obj) { var matches = regex.Match(str); obj.A = matches.Groups[1].Value; obj.B = matches.Groups[2].Value; obj.C = matches.Groups[3].Value; obj.D = matches.Groups[4].Value; }; Action <string, StringPerfObject> handTask = delegate(string str, StringPerfObject obj) { int a = str.IndexOf(','); obj.A = str.Substring(0, a); int b = str.IndexOf(',', a + 1); obj.B = str.Substring(a + 1, b - (a + 1)); int c = str.IndexOf(',', b + 1); obj.C = str.Substring(b + 1, c - (b + 1)); obj.D = str.Substring(c + 1, str.Length - (c + 1)); }; var rand = new Random(); var data = new List <string>(); for (int i = 0; i < 1000000; i++) { data.Add(Guid.NewGuid() + "," + Guid.NewGuid() + "," + Guid.NewGuid() + "," + Guid.NewGuid()); } // Equivalence check foreach (var str in data) { var o1 = new StringPerfObject(); var o2 = new StringPerfObject(); var o3 = new StringPerfObject(); parserTask(str, o1); regexTask(str, o2); handTask(str, o3); if (o1.A != o2.A || o2.A != o3.A) { throw new Exception(); } if (o1.B != o2.B || o2.B != o3.B) { throw new Exception(); } if (o1.C != o2.C || o2.C != o3.C) { throw new Exception(); } if (o1.D != o2.D || o2.D != o3.D) { throw new Exception(); } } var results = new Dictionary <int, List <long> >(); results[0] = new List <long>(); // regex results[1] = new List <long>(); // hand results[2] = new List <long>(); // parser var timer = new Stopwatch(); var speedObj = new StringPerfObject(); for (int i = 0; i < 5; i++) { ghettoGC(); timer.Restart(); foreach (var str in data) { regexTask(str, speedObj); } timer.Stop(); results[0].Add(timer.ElapsedMilliseconds); ghettoGC(); timer.Restart(); foreach (var str in data) { handTask(str, speedObj); } timer.Stop(); results[1].Add(timer.ElapsedMilliseconds); ghettoGC(); timer.Restart(); foreach (var str in data) { parserTask(str, speedObj); } timer.Stop(); results[2].Add(timer.ElapsedMilliseconds); } var medianRegex = results[0].OrderBy(o => o).ElementAt(results[0].Count / 2); var medianHand = results[1].OrderBy(o => o).ElementAt(results[1].Count / 2); var medianParser = results[2].OrderBy(o => o).ElementAt(results[2].Count / 2); Assert.IsTrue(medianRegex > medianHand, "Regex faster than hand rolled; invalid test"); Assert.IsTrue(medianHand > medianParser, "Hand [" + medianHand + "] faster than generated [" + medianParser + "]; bad parser"); }
// full: delete diff post for api remote public async Task Push(string name = "", bool full = false) { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Push to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: { await toLocalFS(remote); } break; case RemoteType.RemoteFS: { throw new NotSupportedException("Not support pushing to remote file system, please push to local file system and sync to remote."); } case RemoteType.Api: { await Connect(name); Logger.LogInformation($"Fetch remote posts."); HashSet <string> remoteIds = (await Remote.PostService.All()).ToHashSet(); foreach (var item in await Local.PostService.GetAllPosts()) { if (item is null) { continue; } Logger.LogInformation($"Loaded {item.Id}: {item.Title}"); if (remoteIds.Contains(item.Id)) { var result = await Remote.PostService.Update(item); if (result) { Logger.LogInformation($"Updated {item.Id}"); } else { Logger.LogError($"Failed to update {item.Id}"); } } else { var result = await Remote.PostService.Create(item); if (result is null) { Logger.LogError($"Failed to create {item.Id}"); } else { Logger.LogInformation($"Created {item.Id}"); } } remoteIds.Remove(item.Id); } if (full) { foreach (var v in remoteIds) { var result = await Remote.PostService.Delete(v); if (result) { Logger.LogInformation($"Deleted {v}."); } else { Logger.LogError($"Failed to deleted {v}."); } } } } break; case RemoteType.Git: { await Connect(name); string tempDist = Path.Join(Environment.CurrentDirectory, "temp/dist"); Logger.LogInformation("Generate data."); await toLocalFS(new RemoteOption { Uri = tempDist, Type = RemoteType.LocalFS, Name = remote.Name }); FSExtensions.CopyDirectory(tempDist, GitTempFolder); Logger.LogInformation("Load git config."); string userName = Option.Properties[$"remote.{remote.Name}.git.username"], password = Option.Properties[$"remote.{remote.Name}.git.password"]; { if (string.IsNullOrEmpty(userName)) { userName = ConsoleExtensions.Input("Input username: "******"Input password: "******"Commit to git."); LibGit2Sharp.Commands.Stage(repo, "*"); var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); repo.Commit(DateTimeOffset.Now.ToString(), signature, signature, new CommitOptions { AllowEmptyCommit = true }); Logger.LogInformation($"Push to {repo.Head.RemoteName}."); PushOptions options = new LibGit2Sharp.PushOptions(); options.CredentialsProvider = new CredentialsHandler( (url, usernameFromUrl, types) => new UsernamePasswordCredentials() { Username = string.IsNullOrEmpty(userName) ? usernameFromUrl : userName, Password = password }); repo.Network.Push(repo.Head, options); } } break; } } else { throw new Exception("No remote"); } async Task toLocalFS(RemoteOption remote) { FSBuilder fsBuilder = new FSBuilder(remote.Uri); fsBuilder.EnsureDirectoryEmpty(); List <Post> posts = new List <Post>(); foreach (var item in await Local.PostService.GetAllPosts()) { if (item is null) { continue; } Logger.LogInformation($"Loaded {item.Id}: {item.Title}"); posts.Add(item); } Logger.LogInformation("Build data."); { BlogOptions options = await Local.GetOptions(); BlogBuilder builder = new BlogBuilder(options, Path.Join(remote.Uri)); await builder.Build(); } { PostRepositoryBuilder builder = new PostRepositoryBuilder(posts, Path.Join(remote.Uri, "posts")); await builder.Build(); } { var baseAddress = Option.Properties[$"remote.{remote.Name}.generator.baseAddress"]; if (!string.IsNullOrEmpty(baseAddress)) { Logger.LogInformation("Build sitemap."); var sub = fsBuilder.CreateSubDirectoryBuilder("Site"); { var siteMapBuilder = await Local.BuildSitemap(baseAddress); using var st = sub.GetFileRewriteStream("sitemap.xml"); using var writer = XmlWriter.Create(st); siteMapBuilder.Build().WriteTo(writer); } Logger.LogInformation("Build feed."); { var feed = await Local.BuildSyndication(baseAddress); using var st = sub.GetFileRewriteStream("atom.xml"); using var writer = XmlWriter.Create(st); feed.GetAtom10Formatter().WriteTo(writer); } } } } }
public async Task Connect(string name = "") { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Connect to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: Remote = new FileSystemBlogService( new PhysicalFileProvider(remote.Uri).AsFileProvider()); break; case RemoteType.RemoteFS: { var client = HttpClientFactory.CreateClient(); client.BaseAddress = new Uri(remote.Uri); Remote = new FileSystemBlogService( new HttpFileProvider(client)); } break; case RemoteType.Api: { var client = HttpClientFactory.CreateClient(); client.BaseAddress = new Uri(remote.Uri); Remote = new ApiBlogService(client); } break; case RemoteType.Git: { FSBuilder builder = new FSBuilder(Environment.CurrentDirectory); Logger.LogInformation("Pull git repository."); try { using var repo = new Repository(GitTempFolder); // Credential information to fetch LibGit2Sharp.PullOptions options = new LibGit2Sharp.PullOptions(); // User information to create a merge commit var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); // Pull LibGit2Sharp.Commands.Pull(repo, signature, options); } catch { builder.EnsureDirectoryEmpty(GitTempFolder); Repository.Clone(remote.Uri, GitTempFolder); } Remote = new FileSystemBlogService( new PhysicalFileProvider(Path.Join(Environment.CurrentDirectory, GitTempFolder)).AsFileProvider()); } break; } Remote.PostService.Context.Token = remote.Token; Option.CurrentRemote = name; await SaveOption(); } else { throw new Exception("No remote"); } }
// full: delete diff post for api remote public async Task Push(string name = "", bool full = false) { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Push to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: { await toLocalFS(remote); } break; case RemoteType.RemoteFS: { throw new NotSupportedException("Not support pushing to remote file system, please push to local file system and sync to remote."); } case RemoteType.Api: { await Connect(name); Logger.LogInformation($"Fetch remote posts."); await Remote.SetOptions(await Local.GetOptions()); await SyncRecordRepository(Local.PostService, Remote.PostService, full); await SyncRecordRepository(Local.PageService, Remote.PageService, full); await SyncRecordRepository(Local.LayoutService, Remote.LayoutService, full); } break; case RemoteType.Git: { await Connect(name); string tempDist = Path.Join(Environment.CurrentDirectory, "temp/dist"); Logger.LogInformation("Generate data."); await toLocalFS(new RemoteOption { Uri = tempDist, Type = RemoteType.LocalFS, Name = remote.Name }); FSExtensions.CopyDirectory(tempDist, GitTempFolder); Logger.LogInformation("Load git config."); string userName = Option.Properties[$"remote.{remote.Name}.git.username"], password = Option.Properties[$"remote.{remote.Name}.git.password"]; { if (string.IsNullOrEmpty(userName)) { userName = ConsoleExtensions.Input("Input username: "******"Input password: "******"Commit to git."); LibGit2Sharp.Commands.Stage(repo, "*"); var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); repo.Commit(DateTimeOffset.Now.ToString(), signature, signature, new CommitOptions { AllowEmptyCommit = true }); Logger.LogInformation($"Push to {repo.Head.RemoteName}."); PushOptions options = new PushOptions { CredentialsProvider = new CredentialsHandler( (url, usernameFromUrl, types) => new UsernamePasswordCredentials() { Username = string.IsNullOrEmpty(userName) ? usernameFromUrl : userName, Password = password }) }; repo.Network.Push(repo.Head, options); } } break; } } else { throw new Exception("No remote"); } async Task toLocalFS(RemoteOption remote) { FSBuilder fsBuilder = new FSBuilder(remote.Uri); fsBuilder.EnsureDirectoryEmpty(); List <Post> posts = new List <Post>(); await foreach (var item in Local.PostService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Post {item.Id}: {item.Title}"); posts.Add(item); } List <Layout> layouts = new List <Layout>(); await foreach (var item in Local.LayoutService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Layout {item.Id}"); layouts.Add(item); } List <Page> pages = new List <Page>(); await foreach (var item in Local.PageService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Page {item.Id}: {item.Title}"); pages.Add(item); } var baseAddress = Option.Properties[$"remote.{remote.Name}.generator.baseAddress"]; List <Data.Models.File> files = new List <Data.Models.File>(); { string path = Path.Join(Environment.CurrentDirectory, AssetsPath); if (Directory.Exists(path)) { foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) { var id = Path.GetRelativePath(Environment.CurrentDirectory, file).Replace('\\', '/'); Data.Models.File f = new Data.Models.File { Id = id, Uri = string.IsNullOrWhiteSpace(baseAddress) ? $"/{id}" : $"{baseAddress.TrimEnd('/')}/{id}" }; files.Add(f); } } } Logger.LogInformation("Build data."); { BlogOptions options = await Local.GetOptions(); BlogBuilder builder = new BlogBuilder(options, Path.Join(remote.Uri)); await builder.Build(); await builder.BuildPosts(posts); await builder.BuildLayouts(layouts); await builder.BuildPages(pages); await builder.BuildFiles(files); } { if (!string.IsNullOrEmpty(baseAddress)) { Logger.LogInformation("Build sitemap."); var sub = fsBuilder.CreateSubDirectoryBuilder("Site"); { var siteMapBuilder = await Local.BuildSitemap(baseAddress); await using var st = sub.GetFileRewriteStream("sitemap.xml"); await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); siteMapBuilder.Build().WriteTo(writer); } Logger.LogInformation("Build feed."); { var feed = await Local.BuildSyndication(baseAddress); await using (var st = sub.GetFileRewriteStream("atom.xml")) { await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); feed.GetAtom10Formatter().WriteTo(writer); } await using (var st = sub.GetFileRewriteStream("rss.xml")) { await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); feed.GetRss20Formatter().WriteTo(writer); } } } } { string assetsPath = Path.Join(Environment.CurrentDirectory, AssetsPath); if (Directory.Exists(assetsPath)) { Logger.LogInformation("Copy assets."); FSExtensions.CopyDirectory(assetsPath, Path.Join(remote.Uri, AssetsPath)); } } } }
public void Errors() { var left = FSBuilder.Take <UnsignedObject>("|", "A").Else((s, o) => { }); var right = FSBuilder.Take <UnsignedObject>("|", "B").Else((s, o) => { }); try { left.Append(right); Assert.Fail("Shouldn't be legal to append two else directives"); } catch (Exception) { } left = FSBuilder.Skip <UnsignedObject>(1).TakeRest("A"); right = FSBuilder.Skip <UnsignedObject>(1).TakeRest("B"); try { left.Append(right); Assert.Fail("Shouldn't be legal to append two take rest directives"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(5, "HelloWorld"); Assert.Fail("Property does not exist"); } catch (Exception) { } // These *shouldn't* throw exceptions FSBuilder.Take <UnsignedObject>("|", "A").Seal()("345212", new UnsignedObject()); FSBuilder.Take <UnsignedObject>(4, "A").Seal()("1", new UnsignedObject()); FSBuilder.Take <UnsignedObject>(1, "A").Take("|", "B").Seal()("asdf", new UnsignedObject()); try { FSBuilder.Skip <UnsignedObject>(1).Back(-4); Assert.Fail("Back shouldn't accept negatives"); } catch (Exception) { } try { FSBuilder.Skip <UnsignedObject>(-4); Assert.Fail("Skip shouldn't accept negatives"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(-4, "A"); Assert.Fail("Take shouldn't accept negatives"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, "Bad"); Assert.Fail("Bad should not be deserializable"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, typeof(string).GetMember("Length")[0]); Assert.Fail("Length is not on UnsignedObject"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, typeof(UnsignedObject).GetMember("ToString")[0]); Assert.Fail("ToString is not a field or property"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, "Hidden"); Assert.Fail("Hidden is not settable"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, "Static"); Assert.Fail("Statis is not an instance property"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, "StaticField"); Assert.Fail("StaticField is not an instance field"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(4, "A", format: "yyyy-mm-dd"); Assert.Fail("A is not a DateTime or TimeSpan"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(",", "DT", format: "asdf"); Assert.Fail("DateTime format string is invalid"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(",", "TS", format: "asdf"); Assert.Fail("TimeSpan format string is invalid"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>(",", "GD", format: "sadf"); Assert.Fail("Guid format string is invalid"); } catch (Exception) { } try { FSBuilder.Take <UnsignedObject>("", "TS"); Assert.Fail("An empty until is invalid"); } catch (Exception) { } }
public void SimpleIntPerf() { var regex = new Regex(@"(\d+),(\d+),(\d+),(\d+)", RegexOptions.Compiled); var parserTask = FSBuilder .Take <IntPerfObject>(",", "A") .Take(",", "B") .Take(",", "C") .TakeRest("D") .Seal(); Action ghettoGC = () => { GC.Collect(); Thread.Sleep(100); }; Action <string, IntPerfObject> regexTask = delegate(string str, IntPerfObject obj) { var matches = regex.Match(str); obj.A = int.Parse(matches.Groups[1].Value); obj.B = int.Parse(matches.Groups[2].Value); obj.C = int.Parse(matches.Groups[3].Value); obj.D = int.Parse(matches.Groups[4].Value); }; Action <string, IntPerfObject> handTask = delegate(string str, IntPerfObject obj) { int a = str.IndexOf(','); obj.A = int.Parse(str.Substring(0, a)); int b = str.IndexOf(',', a + 1); obj.B = int.Parse(str.Substring(a + 1, b - (a + 1))); int c = str.IndexOf(',', b + 1); obj.C = int.Parse(str.Substring(b + 1, c - (b + 1))); obj.D = int.Parse(str.Substring(c + 1, str.Length - (c + 1))); }; var rand = new Random(); var data = new List <string>(); for (int i = 0; i < 1000000; i++) { data.Add(rand.Next() + "," + rand.Next() + "," + rand.Next() + "," + rand.Next()); } // Equivalence check foreach (var str in data) { var o1 = new IntPerfObject(); var o2 = new IntPerfObject(); var o3 = new IntPerfObject(); parserTask(str, o1); regexTask(str, o2); handTask(str, o3); if (o1.A != o2.A || o2.A != o3.A) { throw new Exception(); } if (o1.B != o2.B || o2.B != o3.B) { throw new Exception(); } if (o1.C != o2.C || o2.C != o3.C) { throw new Exception(); } if (o1.D != o2.D || o2.D != o3.D) { throw new Exception(); } } var results = new Dictionary <int, List <long> >(); results[0] = new List <long>(); // regex results[1] = new List <long>(); // hand results[2] = new List <long>(); // parser var timer = new Stopwatch(); var speedObj = new IntPerfObject(); for (int i = 0; i < 5; i++) { ghettoGC(); timer.Restart(); foreach (var str in data) { regexTask(str, speedObj); } timer.Stop(); results[0].Add(timer.ElapsedMilliseconds); ghettoGC(); timer.Restart(); foreach (var str in data) { handTask(str, speedObj); } timer.Stop(); results[1].Add(timer.ElapsedMilliseconds); ghettoGC(); timer.Restart(); foreach (var str in data) { parserTask(str, speedObj); } timer.Stop(); results[2].Add(timer.ElapsedMilliseconds); } var medianRegex = results[0].OrderBy(o => o).ElementAt(results[0].Count / 2); var medianHand = results[1].OrderBy(o => o).ElementAt(results[1].Count / 2); var medianParser = results[2].OrderBy(o => o).ElementAt(results[2].Count / 2); Assert.IsTrue(medianRegex > medianHand); Assert.IsTrue(medianHand > medianParser); }
static Tests() { var step1 = FSBuilder .Until <LogRow>(" "); var step2 = FSBuilder .Take <LogRow>(":", "ClientIp") .Until("[") .Take("]", LogProp("CreationDate"), format: "dd/MMM/yyyy:HH:mm:ss.fff") .Until(" ") .Take(" ", "FrontEnd") .Take("/", LogProp("BackEnd")) .Take(" ", "Server") .Take("/", LogProp("Tq")) .Take("/", "Tw") .Take("/", LogProp("Tc")) .Take("/", "Tr") .Take(" ", LogProp("Tt")) .Take(" ", "ResponseCode") .Take(" ", LogProp("Bytes")) .Until("- ") .Until("- ") .Take(4, "TermState") .Until(" "); var step3 = FSBuilder .Take <LogRow>("/", LogProp("ActConn")) .Take("/", "FeConn") .Take("/", LogProp("BeConn")) .Take("/", "SrvConn") .Take(" ", LogProp("Retries")) .Take("/", "SrvQueue") .Take(" ", LogProp("BackEndQueue")) .Until("{") .Take("|", "Referer") .Take("|", LogProp("UserAgent")) .Take("|", "Host") .Take("|", LogProp("ForwardFor")) .Take("}", "AcceptEncoding") .Until("{") .Take("|", LogProp("ContentEncoding")) .Take("|", "IsPageView") .Take("|", LogProp("SqlDurationMs")) .Take("|", "AccountId") .Take("}", LogProp("RouteName")) .Until("\"") .Take(" ", "Method") .Take(" ", LogProp("Uri")) .Take("\"", "HttpVersion"); FTemplate = step1.Append(step2).Append(step3) .Else( (str, row) => { throw new Exception("Couldn't parse: " + str); } ) .Seal(); }