/LINQToTTree/LINQToTTreeLib.Tests/TestUtils.cs

# · C# · 711 lines · 475 code · 75 blank · 161 comment · 60 complexity · 8ae69278a279ce62ada340ef54197af3 MD5 · raw file

  1. using LinqToTTreeInterfacesLib;
  2. using LINQToTTreeLib.Expressions;
  3. using LINQToTTreeLib.Statements;
  4. using LINQToTTreeLib.Utils;
  5. using Microsoft.VisualStudio.TestTools.UnitTesting;
  6. using NVelocity.App;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Linq.Expressions;
  12. using System.Text.RegularExpressions;
  13. namespace LINQToTTreeLib.Tests
  14. {
  15. /// <summary>
  16. /// Helpers
  17. /// </summary>
  18. public static class TestUtils
  19. {
  20. /// <summary>
  21. /// Dummy loop to help with tests below.
  22. /// </summary>
  23. public class SimpleLoop : LINQToTTreeLib.Statements.StatementInlineBlockBase, IStatementLoop
  24. {
  25. public override IEnumerable<string> CodeItUp()
  26. {
  27. yield return "Dummyloop {";
  28. foreach (var l in RenderInternalCode())
  29. {
  30. yield return " " + l;
  31. }
  32. yield return "}";
  33. }
  34. public override bool TryCombineStatement(IStatement statement, ICodeOptimizationService opt)
  35. {
  36. throw new NotImplementedException();
  37. }
  38. public override void RenameVariable(string origName, string newName)
  39. {
  40. throw new NotImplementedException();
  41. }
  42. public IEnumerable<IDeclaredParameter> LoopIndexVariable
  43. {
  44. get { throw new NotImplementedException(); }
  45. }
  46. }
  47. /// <summary>
  48. /// Look through all the code, and dump out everythign to an ienumerable.
  49. /// </summary>
  50. /// <param name="code"></param>
  51. /// <returns></returns>
  52. public static IEnumerable<string> DumpCode(this GeneratedCode code, bool dumpQM = true)
  53. {
  54. yield return ("Declared Variables:");
  55. foreach (var var in code.CodeBody.DeclaredVariables)
  56. {
  57. string initalValue = "default()";
  58. if (var.InitialValue != null && var.InitialValue != null)
  59. initalValue = var.InitialValue.RawValue;
  60. yield return (var.Type.Name + " " + var.ParameterName + " = " + initalValue + ";");
  61. }
  62. yield return ("Code:");
  63. foreach (var line in code.CodeBody.DumpCode())
  64. {
  65. yield return line;
  66. }
  67. yield return ("");
  68. foreach (var f in code.QMFunctions)
  69. {
  70. yield return (string.Format("Function: {0}", f.Name));
  71. if (dumpQM)
  72. yield return (string.Format(" -> QM: {0}", f.QueryModelText));
  73. yield return (string.Format(" {0} {1} ()", f.ResultType, f.Name));
  74. if (f.StatementBlock != null)
  75. {
  76. foreach (var line in f.StatementBlock.DumpCode())
  77. {
  78. yield return string.Format(" {0}", line);
  79. }
  80. }
  81. else
  82. {
  83. yield return " ** No statements ever set";
  84. }
  85. }
  86. if (code.ResultValue == null)
  87. {
  88. yield return ("Result Variable: <not set (null)>");
  89. }
  90. else
  91. {
  92. yield return ("Result Variable: " + code.ResultValue.ToString());
  93. }
  94. }
  95. /// <summary>
  96. /// Dump the code to the console - for debugging a test...
  97. /// </summary>
  98. /// <param name="code"></param>
  99. public static void DumpCodeToConsole(this GeneratedCode code)
  100. {
  101. code.DumpCode().DumpToConsole();
  102. }
  103. /// <summary>
  104. /// Dump lines to the console.
  105. /// </summary>
  106. /// <param name="lines"></param>
  107. public static void DumpToConsole(this IEnumerable<string> lines)
  108. {
  109. foreach (var item in lines)
  110. {
  111. Console.WriteLine(item);
  112. }
  113. }
  114. /// <summary>
  115. /// Dump the code to the console - for debugging a test...
  116. /// </summary>
  117. /// <param name="code"></param>
  118. public static void DumpCodeToConsole(this IExecutableCode code)
  119. {
  120. foreach (var line in code.DumpCode())
  121. {
  122. Console.WriteLine(line);
  123. }
  124. }
  125. /// <summary>
  126. /// Look through a code block, make sure all the data structures are in order.
  127. /// </summary>
  128. /// <param name="code"></param>
  129. /// <returns></returns>
  130. public static bool CheckCodeBlock(this IExecutableCode code)
  131. {
  132. // Check parent
  133. foreach (var block in code.QueryCode())
  134. {
  135. var r = CheckQueryCodeBlock(block, null);
  136. if (!r)
  137. return r;
  138. }
  139. return true;
  140. }
  141. /// <summary>
  142. /// Check a code block for proper linkages, etc.
  143. /// </summary>
  144. /// <param name="code"></param>
  145. /// <returns></returns>
  146. public static bool CheckCodeBlock(this GeneratedCode code)
  147. {
  148. return CheckQueryCodeBlock(code.CodeBody, null);
  149. }
  150. private static bool CheckQueryCodeBlock(IStatementCompound codeBlock, IStatement parent)
  151. {
  152. if (parent != codeBlock.Parent)
  153. {
  154. Console.WriteLine("ERROR: satement {0} does not point back to proper parent {1}", codeBlock.ToString(), parent.ToString());
  155. return false;
  156. }
  157. foreach (var s in codeBlock.Statements)
  158. {
  159. if (s is IStatementCompound)
  160. {
  161. var r = CheckQueryCodeBlock(s as IStatementCompound, codeBlock);
  162. if (!r)
  163. return r;
  164. }
  165. else
  166. {
  167. if (s.Parent != codeBlock)
  168. {
  169. Console.WriteLine("ERROR: statement {0} does not point back to proper parent {1}", s.ToString(), codeBlock.ToString());
  170. return false;
  171. }
  172. }
  173. }
  174. return true;
  175. }
  176. /// <summary>
  177. /// Dump out info from a executable code dude.
  178. /// </summary>
  179. /// <param name="code"></param>
  180. public static IEnumerable<string> DumpCode(this IExecutableCode code)
  181. {
  182. Console.WriteLine("There are {0} Query Blocks:", code.QueryCode().Count());
  183. foreach (var qb in code.QueryCode())
  184. {
  185. yield return ("Query Block:");
  186. foreach (var line in qb.DumpCode(" "))
  187. {
  188. yield return line;
  189. };
  190. }
  191. foreach (var f in code.Functions)
  192. {
  193. yield return (string.Format("Function: {0}", f.Name));
  194. yield return (string.Format(" {0} {1} ()", f.ResultType.Name, f.Name));
  195. if (f.StatementBlock != null)
  196. {
  197. foreach (var line in f.StatementBlock.DumpCode())
  198. {
  199. yield return string.Format(" {0}", line);
  200. }
  201. }
  202. else
  203. {
  204. yield return " ** No statements ever set";
  205. }
  206. }
  207. if (code.ResultValues == null)
  208. {
  209. yield return ("Result Variable: <not set (null)>");
  210. }
  211. else
  212. {
  213. yield return string.Format("There are {0} result variables.", code.ResultValues.Count());
  214. foreach (var rv in code.ResultValues)
  215. {
  216. yield return string.Format(" Result Variable: {0}", rv.RawValue);
  217. }
  218. }
  219. }
  220. /// <summary>
  221. /// Dump a compound statement
  222. /// </summary>
  223. /// <param name="code"></param>
  224. /// <param name="indent"></param>
  225. /// <returns></returns>
  226. public static IEnumerable<string> DumpCode(this IStatementCompound code, string indent = "")
  227. {
  228. if (code is IBookingStatementBlock)
  229. {
  230. var bs = code as IBookingStatementBlock;
  231. yield return string.Format("{0}There are {1} declared variables", indent, bs.DeclaredVariables.Count());
  232. foreach (var var in bs.DeclaredVariables)
  233. {
  234. string initalValue = "default()";
  235. if (var.InitialValue != null && var.InitialValue != null)
  236. initalValue = var.InitialValue.RawValue;
  237. yield return string.Format(indent + " " + var.Type.Name + " " + var.ParameterName + " = " + initalValue + ";");
  238. }
  239. }
  240. yield return string.Format("{0}Lines of code:", indent);
  241. foreach (var l in code.CodeItUp())
  242. {
  243. yield return string.Format("{0} {1}", indent, l);
  244. }
  245. }
  246. public static IStatementCompound GetDeepestStatementLevel(GeneratedCode target)
  247. {
  248. return GetDeepestStatementLevel(target.CodeBody);
  249. }
  250. public static IStatementCompound GetDeepestStatementLevel(IStatementCompound target)
  251. {
  252. IStatementCompound result = target;
  253. IStatementCompound last = result;
  254. while (last != null)
  255. {
  256. result = last;
  257. if (result.Statements == null)
  258. {
  259. last = null;
  260. }
  261. else
  262. {
  263. last = result.Statements.LastOrDefault() as IStatementCompound;
  264. }
  265. }
  266. return result;
  267. }
  268. /// <summary>
  269. /// Find the statement of a particular type, or return null.
  270. /// </summary>
  271. /// <typeparam name="T"></typeparam>
  272. /// <param name="source"></param>
  273. /// <param name="statementType"></param>
  274. /// <returns></returns>
  275. public static T FindStatement<T>(this IStatementCompound source)
  276. where T : class
  277. {
  278. var here = source.Statements.Where(s => s.GetType() == typeof(T)).FirstOrDefault();
  279. if (here != null)
  280. return (T)here;
  281. return source.Statements
  282. .Where(sc => sc is IStatementCompound)
  283. .Cast<IStatementCompound>()
  284. .Select(s => s.FindStatement<T>())
  285. .Where(found => found != null)
  286. .FirstOrDefault();
  287. }
  288. /// <summary>
  289. /// Returns the statement where the parameter was declared, or null.
  290. /// </summary>
  291. /// <param name="source"></param>
  292. /// <param name="param"></param>
  293. /// <returns></returns>
  294. public static IBookingStatementBlock FindDeclarationStatement(this IStatementCompound source, IDeclaredParameter param)
  295. {
  296. if (source is IBookingStatementBlock)
  297. {
  298. var book = source as IBookingStatementBlock;
  299. var found = book.DeclaredVariables.Where(v => v.ParameterName == param.ParameterName).Any();
  300. if (found)
  301. return book;
  302. }
  303. return source.Statements
  304. .Where(s => s is IStatementCompound)
  305. .Cast<IStatementCompound>()
  306. .Select(s => s.FindDeclarationStatement(param))
  307. .Where(v => v != null)
  308. .FirstOrDefault();
  309. }
  310. /// <summary>
  311. /// Returns all files below the base directory whose name (including extension) match the regex pattern.
  312. /// </summary>
  313. /// <param name="baseDir"></param>
  314. /// <param name="fileExtension"></param>
  315. /// <returns></returns>
  316. public static IEnumerable<FileInfo> FindAllFiles(this DirectoryInfo baseDir, string pattern)
  317. {
  318. var subfiles = from subdir in baseDir.EnumerateDirectories()
  319. from f in subdir.FindAllFiles(pattern)
  320. select f;
  321. Regex matcher = new Regex(pattern);
  322. var goodFiles = from f in baseDir.EnumerateFiles()
  323. where matcher.Match(f.Name).Success
  324. select f;
  325. var allfiles = subfiles.Concat(goodFiles);
  326. return allfiles;
  327. }
  328. public static IBookingStatementBlock GetDeepestBookingLevel(GeneratedCode target)
  329. {
  330. IBookingStatementBlock result = target.CodeBody;
  331. IBookingStatementBlock last = result;
  332. while (last != null)
  333. {
  334. result = last;
  335. last = result.Statements.LastOrDefault() as IBookingStatementBlock;
  336. }
  337. return result;
  338. }
  339. /// <summary>
  340. /// Create a TH1F plot from a stream of objects (with a lambda function to give flexability in conversion).
  341. /// </summary>
  342. /// <typeparam name="TSource"></typeparam>
  343. /// <param name="source"></param>
  344. /// <param name="plotID"></param>
  345. /// <param name="plotTitle"></param>
  346. /// <param name="nbins"></param>
  347. /// <param name="lowBin"></param>
  348. /// <param name="highBin"></param>
  349. /// <param name="getter"></param>
  350. /// <returns></returns>
  351. public static ROOTNET.NTH1F Plot<TSource>
  352. (
  353. this IQueryable<TSource> source,
  354. string plotID, string plotTitle,
  355. int nbins, double lowBin, double highBin,
  356. Expression<Func<TSource, double>> getter)
  357. {
  358. var hParameter = Expression.Parameter(typeof(ROOTNET.NTH1F), "h");
  359. var vParameter = Expression.Parameter(typeof(TSource), "v");
  360. /// h.Fill(getter(v)) is what we want to code up
  361. var callGetter = Expression.Invoke(getter, vParameter);
  362. var fillMethod = typeof(ROOTNET.NTH1F).GetMethod("Fill", new Type[] { typeof(double) });
  363. var callFill = Expression.Call(hParameter, fillMethod, callGetter);
  364. var lambda = Expression.Lambda<Action<ROOTNET.NTH1F, TSource>>(callFill, hParameter, vParameter);
  365. var seed = new ROOTNET.NTH1F(plotID, plotTitle, nbins, lowBin, highBin);
  366. seed.Directory = null;
  367. return source.ApplyToObject(seed, lambda);
  368. }
  369. /// <summary>
  370. /// Generate a TH1F from a stream of T's that can be converted to a double.
  371. /// </summary>
  372. /// <typeparam name="TSource"></typeparam>
  373. /// <param name="source"></param>
  374. /// <param name="plotID"></param>
  375. /// <param name="plotTitle"></param>
  376. /// <param name="nbins"></param>
  377. /// <param name="lowBin"></param>
  378. /// <param name="highBin"></param>
  379. /// <returns></returns>
  380. public static ROOTNET.NTH1F Plot<T>
  381. (
  382. this IQueryable<T> source,
  383. string plotID, string plotTitle,
  384. int nbins, double lowBin, double highBin
  385. )
  386. where T : IConvertible
  387. {
  388. return source.Plot(plotID, plotTitle, nbins, lowBin, highBin, v => Convert.ToDouble(v));
  389. }
  390. /// <summary>
  391. /// Create an output int file... unique so we don't have to regenerate...
  392. /// </summary>
  393. /// <param name="numberOfIter"></param>
  394. /// <returns></returns>
  395. public static Uri CreateFileOf(string filename, Func<ROOTNET.NTTree> maker)
  396. {
  397. FileInfo result = new FileInfo(filename);
  398. var u = new Uri("file://" + result.FullName);
  399. if (result.Exists)
  400. return u;
  401. var f = new ROOTNET.NTFile(filename, "RECREATE");
  402. f.cd();
  403. var tree = maker();
  404. f.Write();
  405. f.Close();
  406. return u;
  407. }
  408. /// <summary>
  409. /// Create an output int file... unique so we don't have to regenerate...
  410. /// </summary>
  411. /// <param name="numberOfIter"></param>
  412. /// <returns></returns>
  413. public static Uri CreateFileOfVectorInt(int numberOfIter, int vectorsize = 10)
  414. {
  415. string filename = string.Format("vectorintonly_{0}_{1}.root", numberOfIter, vectorsize);
  416. return CreateFileOf(filename, () => TTreeParserCPPTests.CreateTrees.CreateTreeWithSimpleSingleVector(numberOfIter, vectorsize));
  417. }
  418. /// <summary>
  419. /// Create an output int file... unique so we don't have to regenerate...
  420. /// </summary>
  421. /// <param name="numberOfIter"></param>
  422. /// <returns></returns>
  423. public static Uri CreateFileOfVectorDouble(int numberOfIter, int vectorsize = 10)
  424. {
  425. string filename = "vectordoubleonly_" + numberOfIter.ToString() + ".root";
  426. return CreateFileOf(filename, () => TTreeParserCPPTests.CreateTrees.CreateTreeWithSimpleSingleDoubleVector(numberOfIter, vectorsize));
  427. }
  428. /// <summary>
  429. /// Create an output int file... unique so we don't have to regenerate...
  430. /// </summary>
  431. /// <param name="numberOfIter"></param>
  432. /// <returns></returns>
  433. public static Uri CreateFileOfIndexedInt(int numberOfIter)
  434. {
  435. string filename = "FileOfIndexedInt" + numberOfIter.ToString() + ".root";
  436. return CreateFileOf(filename, () => TTreeParserCPPTests.CreateTrees.CreateTreeWithIndexedSimpleVector(numberOfIter));
  437. }
  438. /// <summary>
  439. /// Create an output int file... unique so we don't have to regenerate...
  440. /// </summary>
  441. /// <param name="numberOfIter"></param>
  442. /// <returns></returns>
  443. public static Uri CreateFileOfInt(int numberOfIter)
  444. {
  445. string filename = "intonly_" + numberOfIter.ToString() + ".root";
  446. return CreateFileOf(filename, () => TTreeParserCPPTests.CreateTrees.CreateOneIntTree(numberOfIter));
  447. }
  448. /// <summary>
  449. /// Dirt simply test ntuple. Actually matches one that exists on disk.
  450. /// </summary>
  451. public class TestNtupeArr
  452. {
  453. #pragma warning disable 0169
  454. public int[] myvectorofint;
  455. #pragma warning restore 0169
  456. }
  457. /// <summary>
  458. /// Given the root file and the root-tuple name, generate a proxy file
  459. /// </summary>
  460. /// <param name="rootFile"></param>
  461. /// <returns></returns>
  462. public static FileInfo GenerateROOTProxy(Uri rootFile, string rootTupleName)
  463. {
  464. ///
  465. /// First, load up the TTree
  466. ///
  467. var tfile = new ROOTNET.NTFile(rootFile.LocalPath, "READ");
  468. var tree = tfile.Get(rootTupleName) as ROOTNET.Interface.NTTree;
  469. Assert.IsNotNull(tree, "Tree couldn't be found");
  470. ///
  471. /// Create the proxy sub-dir if not there already, and put the dummy macro in there
  472. ///
  473. using (var w = File.CreateText("junk.C"))
  474. {
  475. w.Write("int junk() {return 10.0;}");
  476. w.Close();
  477. }
  478. ///
  479. /// Create the macro proxy now
  480. ///
  481. tree.MakeProxy("scanner", "junk.C", null, "nohist");
  482. return new FileInfo("scanner.h");
  483. }
  484. /// <summary>
  485. /// Reset all the counters, etc., in the LINQ library
  486. /// </summary>
  487. public static void ResetLINQLibrary()
  488. {
  489. var a = ROOTNET.NTROOT.gROOT.GetApplication();
  490. MEFUtilities.MyClassInit();
  491. DummyQueryExectuor.GlobalInitalized = false;
  492. ArrayExpressionParser.ResetParser();
  493. TypeUtils._variableNameCounter = 0;
  494. LINQToTTreeLib.TypeHandlers.ReplacementMethodCalls.TypeHandlerReplacementCall.ClearTypeList();
  495. var eng = new VelocityEngine();
  496. eng.Init();
  497. QueryResultCacheTest.SetupCacheDir();
  498. }
  499. /// <summary>
  500. /// Return the next identifier in the string source after startpattern is found.
  501. /// </summary>
  502. /// <param name="source"></param>
  503. /// <param name="startpattern"></param>
  504. /// <returns></returns>
  505. public static string NextIdentifier(this string source, string startpattern)
  506. {
  507. var sindex = source.IndexOf(startpattern);
  508. if (sindex < 0)
  509. throw new Exception(string.Format("Unable to find '{0}' in string '{1}' - test failure.", startpattern, source));
  510. var matches = Regex.Match(source.Substring(sindex + startpattern.Length), @"\w+");
  511. if (!matches.Success)
  512. throw new Exception(string.Format("Unable to find an identifier in '{0}' after '{1}'", source, startpattern));
  513. return matches.Value;
  514. }
  515. /// <summary>
  516. /// Make sure each function has a return statement on it.
  517. /// </summary>
  518. /// <param name="funcs"></param>
  519. public static void CheckForReturnStatement(this IEnumerable<IQMFuncExecutable> funcs)
  520. {
  521. bool allgood = funcs
  522. .All(f => f.StatementBlock.Statements.LastOrDefault() is StatementReturn);
  523. Assert.IsTrue(allgood, "One function has no top level return!");
  524. }
  525. /// <summary>
  526. /// Find the declaration, then follow scope until it goes out. Then pass every single line of code on.
  527. /// </summary>
  528. /// <param name="source"></param>
  529. /// <param name="decl">The string that tells us we've hit the delcaration</param>
  530. /// <param name="atScopeDeclared">If true, then this is declared in a for loop or similar, otherwise it is declared on a seperate line.</param>
  531. /// <returns></returns>
  532. public static IEnumerable<string> WhereScopeCloses(this IEnumerable<string> source, string decl, bool atScopeDeclared)
  533. {
  534. // First thing is to find the declration.
  535. var next = source.GetEnumerator();
  536. bool found = false;
  537. string line = null;
  538. while (!found)
  539. {
  540. Assert.IsTrue(next.MoveNext(), string.Format("Never found {0} in the code!", decl));
  541. line = next.Current;
  542. var ptr = line.IndexOf(decl);
  543. if (ptr >= 0)
  544. {
  545. found = true;
  546. line = line.Substring(ptr);
  547. }
  548. }
  549. // We found it. Now we start counting brackets to see when we go out of scope.
  550. int depth = 1;
  551. while (depth != 0)
  552. {
  553. while (line.Length > 0)
  554. {
  555. var openb = line.IndexOf('{');
  556. var closeb = line.IndexOf('}');
  557. // If they are both here, process the first one.
  558. if (openb > 0 && closeb > 0)
  559. {
  560. if (openb < closeb)
  561. {
  562. closeb = -1;
  563. }
  564. else
  565. {
  566. openb = -1;
  567. }
  568. }
  569. // If neither went, then we are done with this line.
  570. if (openb < 0 && closeb < 0)
  571. line = "";
  572. int newStart = 0;
  573. if (openb > 0)
  574. {
  575. newStart = openb + 1;
  576. depth++;
  577. if (atScopeDeclared)
  578. {
  579. depth--;
  580. atScopeDeclared = false;
  581. }
  582. }
  583. if (closeb > 0)
  584. {
  585. newStart = closeb + 1;
  586. depth--;
  587. Assert.IsFalse(atScopeDeclared, "Can't start out with a close bracket on the decl without an open bracket");
  588. }
  589. if (newStart > 0)
  590. {
  591. line = line.Substring(newStart);
  592. }
  593. }
  594. Assert.IsTrue(next.MoveNext(), "Ran out of input while waiting for a close bracket");
  595. line = next.Current;
  596. }
  597. // Out of scope, now return all the code!
  598. while (next.MoveNext())
  599. yield return next.Current;
  600. }
  601. /// <summary>
  602. /// Look through the given source code for a particular search string. Somewhere in the search string should be
  603. /// $$ twice. That is the variable name. We will return the first match with that as the variable name.
  604. /// </summary>
  605. /// <param name="source"></param>
  606. /// <param name="searchString"></param>
  607. /// <returns></returns>
  608. public static string FindVariableIn(this IEnumerable<string> source, string searchString)
  609. {
  610. var r = FindVariablesIn(source, searchString).FirstOrDefault();
  611. if (r == null)
  612. Assert.Fail(string.Format("Unable to find '{0}' in the source stream!", searchString));
  613. return r;
  614. }
  615. public static IEnumerable<string> FindVariablesIn(this IEnumerable<string> source, string searchString)
  616. {
  617. var sStr = searchString
  618. .Replace("\\", @"\\")
  619. .Replace("(", @"\(")
  620. .Replace(")", @"\)")
  621. .Replace("[", @"\[")
  622. .Replace("]", @"\]")
  623. .Replace("?", @"\?")
  624. .Replace(".", @"\.")
  625. .Replace("^", @"\^");
  626. sStr = sStr.Replace("$$", @"(?<var>[\w]+)");
  627. var finder = new Regex(sStr);
  628. foreach (var l in source)
  629. {
  630. var m = finder.Match(l);
  631. if (m.Success)
  632. yield return m.Groups["var"].Value;
  633. }
  634. }
  635. }
  636. }