HexViewTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. #nullable enable
  2. using System.Text;
  3. using JetBrains.Annotations;
  4. namespace Terminal.Gui.ViewsTests;
  5. public class HexViewTests
  6. {
  7. [Theory]
  8. [InlineData (0, 4)]
  9. [InlineData (9, 4)]
  10. [InlineData (20, 4)]
  11. [InlineData (24, 4)]
  12. [InlineData (30, 4)]
  13. [InlineData (50, 4)]
  14. public void BytesPerLine_Calculates_Correctly (int width, int expectedBPL)
  15. {
  16. var hv = new HexView (LoadStream (null, out long _)) { Width = width, Height = 10 };
  17. hv.LayoutSubviews ();
  18. Assert.Equal (expectedBPL, hv.BytesPerLine);
  19. }
  20. [Theory]
  21. [InlineData ("01234", 20, 4)]
  22. [InlineData ("012345", 20, 4)]
  23. public void xuz (string str, int width, int expectedBPL)
  24. {
  25. var hv = new HexView (LoadStream (str, out long _)) { Width = width, Height = 10 };
  26. hv.LayoutSubviews ();
  27. Assert.Equal (expectedBPL, hv.BytesPerLine);
  28. }
  29. [Fact]
  30. public void AllowEdits_Edits_ApplyEdits ()
  31. {
  32. var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
  33. // Needed because HexView relies on LayoutComplete to calc sizes
  34. hv.LayoutSubviews ();
  35. Assert.Empty (hv.Edits);
  36. hv.AllowEdits = false;
  37. Assert.True (hv.NewKeyDownEvent (Key.Home));
  38. Assert.False (hv.NewKeyDownEvent (Key.A));
  39. Assert.Empty (hv.Edits);
  40. Assert.Equal (126, hv.Source.Length);
  41. hv.AllowEdits = true;
  42. Assert.True (hv.NewKeyDownEvent (Key.D4));
  43. Assert.True (hv.NewKeyDownEvent (Key.D1));
  44. Assert.Single (hv.Edits);
  45. Assert.Equal (65, hv.Edits.ToList () [0].Value);
  46. Assert.Equal ('A', (char)hv.Edits.ToList () [0].Value);
  47. Assert.Equal (126, hv.Source.Length);
  48. // Appends byte
  49. Assert.True (hv.NewKeyDownEvent (Key.End));
  50. Assert.True (hv.NewKeyDownEvent (Key.D4));
  51. Assert.True (hv.NewKeyDownEvent (Key.D2));
  52. Assert.Equal (2, hv.Edits.Count);
  53. Assert.Equal (66, hv.Edits.ToList () [1].Value);
  54. Assert.Equal ('B', (char)hv.Edits.ToList () [1].Value);
  55. Assert.Equal (126, hv.Source.Length);
  56. hv.ApplyEdits ();
  57. Assert.Empty (hv.Edits);
  58. Assert.Equal (127, hv.Source.Length);
  59. }
  60. [Fact]
  61. public void ApplyEdits_With_Argument ()
  62. {
  63. byte [] buffer = Encoding.Default.GetBytes ("Fest");
  64. var original = new MemoryStream ();
  65. original.Write (buffer, 0, buffer.Length);
  66. original.Flush ();
  67. var copy = new MemoryStream ();
  68. original.Position = 0;
  69. original.CopyTo (copy);
  70. copy.Flush ();
  71. var hv = new HexView (copy) { Width = Dim.Fill (), Height = Dim.Fill () };
  72. // Needed because HexView relies on LayoutComplete to calc sizes
  73. hv.LayoutSubviews ();
  74. var readBuffer = new byte [hv.Source.Length];
  75. hv.Source.Position = 0;
  76. hv.Source.Read (readBuffer);
  77. Assert.Equal ("Fest", Encoding.Default.GetString (readBuffer));
  78. Assert.True (hv.NewKeyDownEvent (Key.D5));
  79. Assert.True (hv.NewKeyDownEvent (Key.D4));
  80. readBuffer [hv.Edits.ToList () [0].Key] = hv.Edits.ToList () [0].Value;
  81. Assert.Equal ("Test", Encoding.Default.GetString (readBuffer));
  82. hv.ApplyEdits (original);
  83. original.Position = 0;
  84. original.Read (buffer);
  85. copy.Position = 0;
  86. copy.Read (readBuffer);
  87. Assert.Equal ("Test", Encoding.Default.GetString (buffer));
  88. Assert.Equal ("Test", Encoding.Default.GetString (readBuffer));
  89. Assert.Equal (Encoding.Default.GetString (buffer), Encoding.Default.GetString (readBuffer));
  90. }
  91. [Fact]
  92. public void Constructors_Defaults ()
  93. {
  94. var hv = new HexView ();
  95. Assert.NotNull (hv.Source);
  96. Assert.IsAssignableFrom<MemoryStream> (hv.Source);
  97. Assert.True (hv.CanFocus);
  98. Assert.True (hv.AllowEdits);
  99. hv = new (new MemoryStream ());
  100. Assert.NotNull (hv.Source);
  101. Assert.IsAssignableFrom<Stream> (hv.Source);
  102. Assert.True (hv.CanFocus);
  103. Assert.True (hv.AllowEdits);
  104. }
  105. [Fact]
  106. public void CursorPosition_Encoding_Default ()
  107. {
  108. var hv = new HexView (LoadStream (null, out _)) { Width = 100, Height = 100 };
  109. Application.Top = new Toplevel ();
  110. Application.Top.Add (hv);
  111. Application.Top.LayoutSubviews ();
  112. Assert.Equal (new (0, 0), hv.CursorPosition);
  113. Assert.Equal (20, hv.BytesPerLine);
  114. Assert.True (hv.NewKeyDownEvent (Key.Tab));
  115. Assert.Equal (new (0, 0), hv.CursorPosition);
  116. Assert.True (hv.NewKeyDownEvent (Key.CursorRight.WithCtrl));
  117. Assert.Equal (hv.CursorPosition.X, hv.BytesPerLine - 1);
  118. Assert.True (hv.NewKeyDownEvent (Key.Home));
  119. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  120. Assert.Equal (new (1, 0), hv.CursorPosition);
  121. Assert.True (hv.NewKeyDownEvent (Key.CursorDown));
  122. Assert.Equal (new (1, 1), hv.CursorPosition);
  123. Assert.True (hv.NewKeyDownEvent (Key.End));
  124. Assert.Equal (new (2, 2), hv.CursorPosition);
  125. int col = hv.CursorPosition.X;
  126. int line = hv.CursorPosition.Y;
  127. int offset = line * (hv.BytesPerLine - col);
  128. Assert.Equal (hv.Address, col * line + offset);
  129. Application.Top.Dispose ();
  130. Application.ResetState (true);
  131. }
  132. [Fact]
  133. public void CursorPosition_Encoding_Unicode ()
  134. {
  135. var hv = new HexView (LoadStream (null, out _, true)) { Width = Dim.Fill (), Height = Dim.Fill () };
  136. Application.Top = new Toplevel ();
  137. Application.Top.Add (hv);
  138. hv.LayoutSubviews ();
  139. Assert.Equal (new (0, 0), hv.CursorPosition);
  140. Assert.True (hv.NewKeyDownEvent (Key.Tab));
  141. Assert.True (hv.NewKeyDownEvent (Key.CursorRight.WithCtrl));
  142. Assert.Equal (hv.CursorPosition.X, hv.BytesPerLine);
  143. Assert.True (hv.NewKeyDownEvent (Key.Home));
  144. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  145. Assert.Equal (new (2, 1), hv.CursorPosition);
  146. Assert.True (hv.NewKeyDownEvent (Key.CursorDown));
  147. Assert.Equal (new (2, 2), hv.CursorPosition);
  148. Assert.True (hv.NewKeyDownEvent (Key.End));
  149. int col = hv.CursorPosition.X;
  150. int line = hv.CursorPosition.Y;
  151. int offset = (line - 1) * (hv.BytesPerLine - col);
  152. Assert.Equal (hv.Address, col * line + offset);
  153. Application.Top.Dispose ();
  154. Application.ResetState (true);
  155. }
  156. [Fact]
  157. public void DiscardEdits_Method ()
  158. {
  159. var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
  160. // Needed because HexView relies on LayoutComplete to calc sizes
  161. hv.LayoutSubviews ();
  162. Assert.True (hv.NewKeyDownEvent (Key.D4));
  163. Assert.True (hv.NewKeyDownEvent (Key.D1));
  164. Assert.Single (hv.Edits);
  165. Assert.Equal (65, hv.Edits.ToList () [0].Value);
  166. Assert.Equal ('A', (char)hv.Edits.ToList () [0].Value);
  167. Assert.Equal (126, hv.Source.Length);
  168. hv.DiscardEdits ();
  169. Assert.Empty (hv.Edits);
  170. }
  171. [Fact]
  172. public void DisplayStart_Source ()
  173. {
  174. var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
  175. // Needed because HexView relies on LayoutComplete to calc sizes
  176. hv.LayoutSubviews ();
  177. Assert.Equal (0, hv.DisplayStart);
  178. Assert.True (hv.NewKeyDownEvent (Key.PageDown));
  179. Assert.Equal (4 * hv.Frame.Height, hv.DisplayStart);
  180. Assert.Equal (hv.Source.Length, hv.Source.Position);
  181. Assert.True (hv.NewKeyDownEvent (Key.End));
  182. // already on last page and so the DisplayStart is the same as before
  183. Assert.Equal (4 * hv.Frame.Height, hv.DisplayStart);
  184. Assert.Equal (hv.Source.Length, hv.Source.Position);
  185. }
  186. [Fact]
  187. public void Edited_Event ()
  188. {
  189. var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
  190. // Needed because HexView relies on LayoutComplete to calc sizes
  191. hv.LayoutSubviews ();
  192. KeyValuePair<long, byte> keyValuePair = default;
  193. hv.Edited += (s, e) => keyValuePair = new (e.Address, e.NewValue);
  194. Assert.True (hv.NewKeyDownEvent (Key.D4));
  195. Assert.True (hv.NewKeyDownEvent (Key.D6));
  196. Assert.Equal (0, (int)keyValuePair.Key);
  197. Assert.Equal (70, keyValuePair.Value);
  198. Assert.Equal ('F', (char)keyValuePair.Value);
  199. }
  200. [Fact]
  201. public void Exceptions_Tests ()
  202. {
  203. Assert.Throws<ArgumentNullException> (() => new HexView (null));
  204. Assert.Throws<ArgumentException> (() => new HexView (new NonSeekableStream (new MemoryStream ())));
  205. }
  206. [Fact]
  207. public void KeyBindings_Test_Movement_LeftSide ()
  208. {
  209. var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 };
  210. Application.Top = new Toplevel ();
  211. Application.Top.Add (hv);
  212. hv.LayoutSubviews ();
  213. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Length);
  214. Assert.Equal (0, hv.Address);
  215. Assert.Equal (4, hv.BytesPerLine);
  216. // right side only needed to press one time
  217. Assert.True (hv.NewKeyDownEvent (Key.Tab));
  218. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  219. Assert.Equal (1, hv.Address);
  220. Assert.True (hv.NewKeyDownEvent (Key.CursorLeft));
  221. Assert.Equal (0, hv.Address);
  222. Assert.True (hv.NewKeyDownEvent (Key.CursorDown));
  223. Assert.Equal (4, hv.Address);
  224. Assert.True (hv.NewKeyDownEvent (Key.CursorUp));
  225. Assert.Equal (0, hv.Address);
  226. Assert.True (hv.NewKeyDownEvent (Key.PageDown));
  227. Assert.Equal (40, hv.Address);
  228. Assert.True (hv.NewKeyDownEvent (Key.PageUp));
  229. Assert.Equal (0, hv.Address);
  230. Assert.True (hv.NewKeyDownEvent (Key.End));
  231. Assert.Equal (MEM_STRING_LENGTH, hv.Address);
  232. Assert.True (hv.NewKeyDownEvent (Key.Home));
  233. Assert.Equal (0, hv.Address);
  234. Assert.True (hv.NewKeyDownEvent (Key.CursorRight.WithCtrl));
  235. Assert.Equal (3, hv.Address);
  236. Assert.True (hv.NewKeyDownEvent (Key.CursorLeft.WithCtrl));
  237. Assert.Equal (0, hv.Address);
  238. Assert.True (hv.NewKeyDownEvent (Key.CursorDown.WithCtrl));
  239. Assert.Equal (36, hv.Address);
  240. Assert.True (hv.NewKeyDownEvent (Key.CursorUp.WithCtrl));
  241. Assert.Equal (0, hv.Address);
  242. Application.Top.Dispose ();
  243. Application.ResetState (true);
  244. }
  245. [Fact]
  246. public void Position_Using_Encoding_Default ()
  247. {
  248. var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 20 };
  249. hv.LayoutSubviews ();
  250. // Needed because HexView relies on LayoutComplete to calc sizes
  251. hv.LayoutSubviews ();
  252. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Length);
  253. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  254. Assert.Equal (0, hv.Address);
  255. // left side needed to press twice
  256. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  257. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  258. Assert.Equal (1, hv.Address);
  259. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  260. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  261. Assert.Equal (2, hv.Address);
  262. // right side only needed to press one time
  263. Assert.True (hv.NewKeyDownEvent (Key.Tab));
  264. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  265. Assert.Equal (2, hv.Address);
  266. Assert.True (hv.NewKeyDownEvent (Key.CursorLeft));
  267. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  268. Assert.Equal (1, hv.Address);
  269. // last position is equal to the source length
  270. Assert.True (hv.NewKeyDownEvent (Key.End));
  271. Assert.Equal (MEM_STRING_LENGTH, hv.Source.Position);
  272. Assert.Equal (64, hv.Address);
  273. Assert.Equal (hv.Address - 1, hv.Source.Length);
  274. }
  275. [Fact]
  276. public void Position_Using_Encoding_Unicode ()
  277. {
  278. var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
  279. // Needed because HexView relies on LayoutComplete to calc sizes
  280. hv.LayoutSubviews ();
  281. Assert.Equal (126, hv.Source.Length);
  282. Assert.Equal (126, hv.Source.Position);
  283. Assert.Equal (1, hv.Address);
  284. // left side needed to press twice
  285. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  286. Assert.Equal (126, hv.Source.Position);
  287. Assert.Equal (1, hv.Address);
  288. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  289. Assert.Equal (126, hv.Source.Position);
  290. Assert.Equal (2, hv.Address);
  291. // right side only needed to press one time
  292. Assert.True (hv.NewKeyDownEvent (Key.Tab));
  293. Assert.Equal (126, hv.Source.Position);
  294. Assert.Equal (2, hv.Address);
  295. Assert.True (hv.NewKeyDownEvent (Key.CursorLeft));
  296. Assert.Equal (126, hv.Source.Position);
  297. Assert.Equal (1, hv.Address);
  298. // last position is equal to the source length
  299. Assert.True (hv.NewKeyDownEvent (Key.End));
  300. Assert.Equal (126, hv.Source.Position);
  301. Assert.Equal (127, hv.Address);
  302. Assert.Equal (hv.Address - 1, hv.Source.Length);
  303. }
  304. [Fact]
  305. public void PositionChanged_Event ()
  306. {
  307. var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 };
  308. Application.Top = new Toplevel ();
  309. Application.Top.Add (hv);
  310. Application.Top.LayoutSubviews ();
  311. HexViewEventArgs hexViewEventArgs = null;
  312. hv.PositionChanged += (s, e) => hexViewEventArgs = e;
  313. Assert.Equal (12, hv.BytesPerLine);
  314. Assert.True (hv.NewKeyDownEvent (Key.CursorRight)); // left side must press twice
  315. Assert.True (hv.NewKeyDownEvent (Key.CursorRight));
  316. Assert.True (hv.NewKeyDownEvent (Key.CursorDown));
  317. Assert.Equal (12, hexViewEventArgs.BytesPerLine);
  318. Assert.Equal (new (2, 2), hexViewEventArgs.CursorPosition);
  319. Assert.Equal (14, hexViewEventArgs.Address);
  320. Application.Top.Dispose ();
  321. Application.ResetState (true);
  322. }
  323. [Fact]
  324. public void Source_Sets_DisplayStart_And_Position_To_Zero_If_Greater_Than_Source_Length ()
  325. {
  326. var hv = new HexView (LoadStream (null, out _)) { Width = 10, Height = 5 };
  327. Application.Top = new Toplevel ();
  328. Application.Top.Add (hv);
  329. hv.LayoutSubviews ();
  330. Assert.True (hv.NewKeyDownEvent (Key.End));
  331. Assert.Equal (62, hv.DisplayStart);
  332. Assert.Equal (64, hv.Address);
  333. hv.Source = new MemoryStream ();
  334. Assert.Equal (0, hv.DisplayStart);
  335. Assert.Equal (0, hv.Address - 1);
  336. hv.Source = LoadStream (null, out _);
  337. hv.Width = Dim.Fill ();
  338. hv.Height = Dim.Fill ();
  339. Application.Top.LayoutSubviews ();
  340. Assert.Equal (0, hv.DisplayStart);
  341. Assert.Equal (0, hv.Address - 1);
  342. Assert.True (hv.NewKeyDownEvent (Key.End));
  343. Assert.Equal (0, hv.DisplayStart);
  344. Assert.Equal (64, hv.Address);
  345. hv.Source = new MemoryStream ();
  346. Assert.Equal (0, hv.DisplayStart);
  347. Assert.Equal (0, hv.Address - 1);
  348. Application.Top.Dispose ();
  349. Application.ResetState (true);
  350. }
  351. private const string MEM_STRING = "Hello world.\nThis is a test of the Emergency Broadcast System.\n";
  352. private const int MEM_STRING_LENGTH = 63;
  353. private Stream LoadStream (string? memString, out long numBytesInMemString, bool unicode = false)
  354. {
  355. var stream = new MemoryStream ();
  356. byte [] bArray;
  357. Assert.Equal (MEM_STRING_LENGTH, MEM_STRING.Length);
  358. if (memString is null)
  359. {
  360. memString = MEM_STRING;
  361. }
  362. if (unicode)
  363. {
  364. bArray = Encoding.Unicode.GetBytes (memString);
  365. }
  366. else
  367. {
  368. bArray = Encoding.Default.GetBytes (memString);
  369. }
  370. numBytesInMemString = bArray.Length;
  371. stream.Write (bArray);
  372. return stream;
  373. }
  374. private class NonSeekableStream (Stream baseStream) : Stream
  375. {
  376. public override bool CanRead => baseStream.CanRead;
  377. public override bool CanSeek => false;
  378. public override bool CanWrite => baseStream.CanWrite;
  379. public override long Length => throw new NotSupportedException ();
  380. public override long Position
  381. {
  382. get => baseStream.Position;
  383. set => throw new NotSupportedException ();
  384. }
  385. public override void Flush () { baseStream.Flush (); }
  386. public override int Read (byte [] buffer, int offset, int count) { return baseStream.Read (buffer, offset, count); }
  387. public override long Seek (long offset, SeekOrigin origin) { throw new NotImplementedException (); }
  388. public override void SetLength (long value) { throw new NotSupportedException (); }
  389. public override void Write (byte [] buffer, int offset, int count) { baseStream.Write (buffer, offset, count); }
  390. }
  391. }