]> git.mdlowis.com Git - projs/tide.git/commitdiff
Added unit tests for buf.c and tweaked xresources sample file
authorMichael D. Lowis <mike.lowis@gentex.com>
Thu, 22 Jun 2017 14:40:16 +0000 (10:40 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Thu, 22 Jun 2017 14:40:16 +0000 (10:40 -0400)
Makefile
TODO.md
XResources
inc/edit.h
lib/buf.c
lib/config.c
tests/lib/buf.c

index 4f2da711131a6dd5aaff537cca797d98c7824bf9..a83c4a1b9659b8401457272f9ce8cb645f7abbc3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,7 @@ docs:
 
 clean:
        find . -name '*.[oad]' -delete
+       find . \( -name '*.gcno' -o -name '*.gcda' \) -delete
        $(RM) pick tide xcpd term tests/libedit
        $(RM) $(TEST_BINS)
 
diff --git a/TODO.md b/TODO.md
index 6e9c2ee1f684bec0480cffa6e020d4cf97219243..62d1e621adee6c14841ca5c924c6934f0ef7485a 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -2,16 +2,16 @@
 
 Up Next:
 
+* implement transaction control in buf.c
 * highlight all matches of search term
 * highlight classes of identifiers
-* implement transaction control in buf.c
 * Add a way to CD using a builtin (buffers will track original dir)
 * shortcut to jump to previous edit
 * add command line flags to toggle options (Tabs, Indent, etc..)
 * move by words is inconsistent. Example:
     var infoId = 'readerinfo'+reader.id;
 * ignore the menu key or map it to control
-
+   
 The Future:
 
 * Ctrl+/ shortcut to comment/uncomment based on syntax
index 29dda5696ebc5b3b0599c25543ca8374ac21e512..343cc7060813e868423c6ac4eee75f7d4c922815 100644 (file)
@@ -5,7 +5,7 @@ tide.ui.font:         Monaco:size=10:antialias=true:autohint=true
 tide.ui.line_spacing: 0
 #else
 tide.ui.font:         Liberation Mono:pixelsize=14:antialias=true:autohint=true
-tide.ui.line_spacing: 2
+tide.ui.line_spacing: 1
 #endif
 tide.ui.tags:         'Quit Save Undo Redo Cut Copy Paste | Find '
 tide.ui.width:        640
@@ -23,32 +23,50 @@ tide.input.scroll_lines:  4
 tide.input.click_time:    500
 tide.input.max_scan_dist: 8192
 
-! Color Palette Definition
-tide.palette.00: 0xff002b36
-tide.palette.01: 0xff073642
-tide.palette.02: 0xff586e75
-tide.palette.03: 0xff657b83
-tide.palette.04: 0xff839496
-tide.palette.05: 0xff93a1a1
-tide.palette.06: 0xffeee8d5
-tide.palette.07: 0xfffdf6e3
-tide.palette.08: 0xffb58900
-tide.palette.09: 0xffcb4b16
-tide.palette.10: 0xffdc322f
-tide.palette.11: 0xffd33682
-tide.palette.12: 0xff6c71c4
-tide.palette.13: 0xff268bd2
-tide.palette.14: 0xff2aa198
-tide.palette.15: 0xff859900
+! Color Palette Definition (Solarized Dark)
+tide.palette.00: 0xff002b36 ! 00 (0) - Base03
+tide.palette.01: 0xff073642 ! 01 (1) - Base02
+tide.palette.02: 0xff586e75 ! 02 (2) - Base01
+tide.palette.03: 0xff657b83 ! 03 (3) - Base00
+tide.palette.04: 0xff839496 ! 04 (4) - Base0
+tide.palette.05: 0xff93a1a1 ! 05 (5) - Base1
+tide.palette.06: 0xffeee8d5 ! 06 (6) - Base2
+tide.palette.07: 0xfffdf6e3 ! 07 (7) - Base3
+tide.palette.08: 0xffb58900 ! 08 (8) - Yellow
+tide.palette.09: 0xffcb4b16 ! 09 (9) - Orange
+tide.palette.10: 0xffdc322f ! 10 (A) - Red
+tide.palette.11: 0xffd33682 ! 11 (B) - Magenta
+tide.palette.12: 0xff6c71c4 ! 12 (C) - Violet
+tide.palette.13: 0xff268bd2 ! 13 (D) - Blue
+tide.palette.14: 0xff2aa198 ! 14 (E) - Cyan
+tide.palette.15: 0xff859900 ! 15 (F) - Green
+
+!! Color Palette Definition (Solarized Light)
+!tide.palette.00: 0xfffdf6e3 ! 00 (0) - Base03
+!tide.palette.01: 0xffeee8d5 ! 01 (1) - Base02
+!tide.palette.02: 0xff93a1a1 ! 02 (2) - Base01
+!tide.palette.03: 0xff839496 ! 03 (3) - Base00
+!tide.palette.04: 0xff657b83 ! 04 (4) - Base0
+!tide.palette.05: 0xff586e75 ! 05 (5) - Base1
+!tide.palette.06: 0xff073642 ! 06 (6) - Base2
+!tide.palette.07: 0xff002b36 ! 07 (7) - Base3
+!tide.palette.08: 0xffb58900 ! 08 (8) - Yellow
+!tide.palette.09: 0xffcb4b16 ! 09 (9) - Orange
+!tide.palette.10: 0xffdc322f ! 10 (A) - Red
+!tide.palette.11: 0xffd33682 ! 11 (B) - Magenta
+!tide.palette.12: 0xff6c71c4 ! 12 (C) - Violet
+!tide.palette.13: 0xff268bd2 ! 13 (D) - Blue
+!tide.palette.14: 0xff2aa198 ! 14 (E) - Cyan
+!tide.palette.15: 0xff859900 ! 15 (F) - Green
 
 ! Background and UI Colors
-tide.colors.ruler:      0x01
-tide.colors.gutter:     0x01
-tide.colors.bkg_tags:   0x01
-tide.colors.bkg_edit:   0x00
-tide.colors.bkg_scroll: 0x03
-tide.colors.bkg_thumb:  0x00
-tide.colors.border:     0x03
+tide.colors.ruler:      0x1
+tide.colors.gutter:     0x1
+tide.colors.bkg_tags:   0x1
+tide.colors.bkg_edit:   0x0
+tide.colors.bkg_scroll: 0x3
+tide.colors.bkg_thumb:  0x0
+tide.colors.border:     0x3
 
 ! Base Text Colors
 tide.colors.text.cursor:   0x0007
@@ -57,7 +75,7 @@ tide.colors.text.selected: 0x0400
 tide.colors.text.gutter:   0x0003
 tide.colors.text.currline: 0x0D07
 
-! Syntax Colors
+! Syntax Highlighting Colors
 tide.colors.syntax.normal:       0x0004
 tide.colors.syntax.comment:      0x0002
 tide.colors.syntax.constant:     0x000E
index 4853ab3242d35707aa1b2455a03eac912263175f..ceaf0d62241f91ad1665eec371b0a4720b5745d7 100644 (file)
@@ -94,7 +94,6 @@ void buf_getblock(Buf* buf, Rune beg, Rune end, Sel* sel);
 size_t buf_byrune(Buf* buf, size_t pos, int count);
 size_t buf_byword(Buf* buf, size_t pos, int count);
 size_t buf_byline(Buf* buf, size_t pos, int count);
-void buf_find(Buf* buf, int dir, size_t* beg, size_t* end);
 void buf_findstr(Buf* buf, int dir, char* str, size_t* beg, size_t* end);
 void buf_lastins(Buf* buf, size_t* beg, size_t* end);
 size_t buf_setln(Buf* buf, size_t line);
@@ -236,7 +235,7 @@ char* cmdwriteread(char** cmd, char* text, char** err);
 
 /* Configuration Data
  *****************************************************************************/
-enum {
+enum { /* Configuration Variables */
     FontString = 0, TagString, WinWidth, WinHeight, LineSpacing, LineNumbers,
     RulerColumn, EventTimeout, CopyIndent, TrimOnSave, ExpandTabs, TabWidth,
     ScrollLines, DblClickTime, MaxScanDist,
index d5b8df1e73e4927a8a2e14d114993af27b555107..4b1313478960355f9d4318c445256b1ef4652a04 100644 (file)
--- a/lib/buf.c
+++ b/lib/buf.c
@@ -345,25 +345,6 @@ size_t buf_byline(Buf* buf, size_t pos, int count) {
     return pos;
 }
 
-void buf_find(Buf* buf, int dir, size_t* beg, size_t* end) {
-    size_t dbeg = *beg, dend = *end;
-    size_t mbeg = dbeg+dir, mend = dend+dir;
-    size_t mlen = dend - dbeg;
-    while (true) {
-        if ((buf_get(buf, mbeg)   == buf_get(buf, dbeg)) &&
-            (buf_get(buf, mend-1) == buf_get(buf, dend-1)) &&
-            (0 == range_match(buf, dbeg, dend, mbeg, mend)))
-        {
-            *beg = mbeg;
-            *end = mend;
-            break;
-        }
-        mbeg += dir, mend += dir;
-        if (mend > buf_end(buf))
-            mbeg = (dir < 0 ? buf_end(buf)-mlen : 0), mend = mbeg+mlen;
-    }
-}
-
 void buf_findstr(Buf* buf, int dir, char* str, size_t* beg, size_t* end) {
     if (!str) return;
     Rune* runes = charstorunes(str);
index fb772016a3c2b1db1b512d34e9150652caf48c6d..270e70d2d5f5110b8cb23459ed3aa78312fc3943 100644 (file)
@@ -24,7 +24,7 @@ struct {
 #else
     [FontString] = { "tide.ui.font", STRING, {
         .str = "Liberation Mono:pixelsize=14:antialias=true:autohint=true" } },
-    [LineSpacing]  = { "tide.ui.line_spacing", INTEGER, { .num = 2    } },
+    [LineSpacing]  = { "tide.ui.line_spacing", INTEGER, { .num = 1    } },
 #endif
 
     /* user interface related options */
index 80eb6000745a40d1559c40011d0dac1ad695ae3c..f9555ac4e1855df4b8bf8fdecdaca5474ce68172 100644 (file)
@@ -20,19 +20,205 @@ static void set_buffer_text(char* str) {
 
 static bool buf_text_eq(char* str) {
     for (unsigned i = 0; i < buf_end(&TestBuf); i++) {
-        if ((Rune)*(str++) != buf_get(&TestBuf, i))
+        printf("'%c'", buf_get(&TestBuf, i));
+        if ((Rune)*(str++) != buf_get(&TestBuf, i)) {
+            printf("\n");
             return false;
+        }
     }
+    printf("\n");
     return true;
 }
 
 TEST_SUITE(BufferTests) {
     /* Initializing
      *************************************************************************/
+    TEST(buf_init should initialize an empty buffer) {
+        Buf buf = {0};
+        buf_init(&buf, (void*)0x12345678);
+        CHECK(buf.modified    == false);
+        CHECK(buf.expand_tabs == config_get_bool(ExpandTabs));
+        CHECK(buf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(buf.charset     == UTF_8);
+        CHECK(buf.crlf        == 0);
+        CHECK(buf.bufsize     == 8192);
+        CHECK(buf.bufstart    != NULL);
+        CHECK(buf.bufend      == buf.bufstart + buf.bufsize);
+        CHECK(buf.gapstart    == buf.bufstart);
+        CHECK(buf.gapend      == buf.bufend);
+        CHECK(buf.undo        == NULL);
+        CHECK(buf.redo        == NULL);
+        CHECK(buf.errfn       == (void*)0x12345678);
+        CHECK(buf.nlines      == 0);
+    }
+    
+    TEST(buf_init shoud free old buffer and reinitialize) {
+        Buf buf = {0};
+        buf_init(&buf, onerror);
+        buf_insert(&buf, false, 0, 'a');
+        buf_init(&buf, (void*)0x12345678);
+        CHECK(buf.modified    == false);
+        CHECK(buf.expand_tabs == config_get_bool(ExpandTabs));
+        CHECK(buf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(buf.charset     == UTF_8);
+        CHECK(buf.crlf        == 0);
+        CHECK(buf.bufsize     == 8192);
+        CHECK(buf.bufstart    != NULL);
+        CHECK(buf.bufend      == buf.bufstart + buf.bufsize);
+        CHECK(buf.gapstart    == buf.bufstart);
+        CHECK(buf.gapend      == buf.bufend);
+        CHECK(buf.undo        == NULL);
+        CHECK(buf.redo        == NULL);
+        CHECK(buf.errfn       == (void*)0x12345678);
+        CHECK(buf.nlines      == 0);
+    }
+    
     /* Loading
      *************************************************************************/
+    TEST(buf_load should load a UTF-8 file from disk) {
+        buf_init(&TestBuf, NULL);
+        size_t pos = buf_load(&TestBuf, "testdocs/lorem.txt");
+        CHECK(pos                 == 0);
+        CHECK(TestBuf.modified    == false);
+        CHECK(TestBuf.expand_tabs == true);
+        CHECK(TestBuf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(TestBuf.charset     == UTF_8);
+        CHECK(TestBuf.crlf        == 0);
+        CHECK(TestBuf.bufsize     == 65536);
+        CHECK(TestBuf.undo        == NULL);
+        CHECK(TestBuf.redo        == NULL);
+        CHECK(TestBuf.errfn       == NULL);
+        CHECK(TestBuf.nlines      == 998);
+        CHECK(!strcmp(TestBuf.path, "testdocs/lorem.txt"));
+    }
+    
+    TEST(buf_load should load a non UTF-8 file from disk) {
+        buf_init(&TestBuf, NULL);
+        size_t pos = buf_load(&TestBuf, "testdocs/waf");
+        CHECK(pos                 == 0);
+        CHECK(TestBuf.modified    == false);
+        CHECK(TestBuf.expand_tabs == false);
+        CHECK(TestBuf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(TestBuf.charset     == BINARY);
+        CHECK(TestBuf.crlf        == 0);
+        CHECK(TestBuf.bufsize     == 131072);
+        CHECK(TestBuf.undo        == NULL);
+        CHECK(TestBuf.redo        == NULL);
+        CHECK(TestBuf.errfn       == NULL);
+        CHECK(TestBuf.nlines      == 169);
+        CHECK(!strcmp(TestBuf.path, "testdocs/waf"));
+    }
+    
+    TEST(buf_load should load a file from disk and jump to a specific line) {
+        buf_init(&TestBuf, NULL);
+        size_t pos = buf_load(&TestBuf, "testdocs/lorem.txt:2");
+        CHECK(pos                 == 70);
+        CHECK(TestBuf.modified    == false);
+        CHECK(TestBuf.expand_tabs == true);
+        CHECK(TestBuf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(TestBuf.charset     == UTF_8);
+        CHECK(TestBuf.crlf        == 0);
+        CHECK(TestBuf.bufsize     == 65536);
+        CHECK(TestBuf.undo        == NULL);
+        CHECK(TestBuf.redo        == NULL);
+        CHECK(TestBuf.errfn       == NULL);
+        CHECK(TestBuf.nlines      == 998);
+        CHECK(!strcmp(TestBuf.path, "testdocs/lorem.txt"));
+    }
+    
+    TEST(buf_load should remove ./ from file path) {
+        buf_init(&TestBuf, NULL);
+        size_t pos = buf_load(&TestBuf, "./testdocs/lorem.txt");
+        CHECK(pos                 == 0);
+        CHECK(TestBuf.modified    == false);
+        CHECK(TestBuf.expand_tabs == true);
+        CHECK(TestBuf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(TestBuf.charset     == UTF_8);
+        CHECK(TestBuf.crlf        == 0);
+        CHECK(TestBuf.bufsize     == 65536);
+        CHECK(TestBuf.undo        == NULL);
+        CHECK(TestBuf.redo        == NULL);
+        CHECK(TestBuf.errfn       == NULL);
+        CHECK(TestBuf.nlines      == 998);
+        CHECK(!strcmp(TestBuf.path, "testdocs/lorem.txt"));
+    }
+    
+    TEST(buf_reload should reload the file from disk) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/waf");
+        TestBuf.path = "testdocs/lorem.txt";
+        buf_reload(&TestBuf);
+        CHECK(TestBuf.modified    == false);
+        CHECK(TestBuf.expand_tabs == true);
+        CHECK(TestBuf.copy_indent == config_get_bool(CopyIndent));
+        CHECK(TestBuf.charset     == UTF_8);
+        CHECK(TestBuf.crlf        == 0);
+        CHECK(TestBuf.bufsize     == 65536);
+        CHECK(TestBuf.undo        == NULL);
+        CHECK(TestBuf.redo        == NULL);
+        CHECK(TestBuf.errfn       == NULL);
+        CHECK(TestBuf.nlines      == 998);
+        CHECK(!strcmp(TestBuf.path, "testdocs/lorem.txt"));
+    }
+    
     /* Saving
      *************************************************************************/
+    TEST(buf_save should save a UTF-8 file to disk) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/lorem.txt");
+        TestBuf.modified = true;
+        buf_save(&TestBuf);
+        CHECK(TestBuf.modified == false);
+    }
+    
+    TEST(buf_save should save a non UTF-8 file to disk) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/waf");
+        TestBuf.modified = true;
+        buf_save(&TestBuf);
+        CHECK(TestBuf.modified == false);
+    }
+    
+    TEST(buf_save should save a file to disk with unix line endings) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/lf.txt");
+        TestBuf.modified = true;
+        buf_save(&TestBuf);
+        CHECK(TestBuf.modified == false);
+    }
+    
+    TEST(buf_save should save a file to disk with dos line endings) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/crlf.txt");
+        TestBuf.modified = true;
+        buf_save(&TestBuf);
+        CHECK(TestBuf.modified == false);
+    }
+    
+    TEST(buf_save should make sure unix file ends witn newline) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/lf.txt");
+        TestBuf.modified = true;
+        size_t end = buf_end(&TestBuf);
+        buf_delete(&TestBuf, end-1, end);
+        CHECK(end-1 == buf_end(&TestBuf));
+        buf_save(&TestBuf);
+        CHECK(end == buf_end(&TestBuf));
+        CHECK(TestBuf.modified == false);
+    }
+
+    TEST(buf_save should make sure dos file ends witn newline) {
+        buf_init(&TestBuf, NULL);
+        buf_load(&TestBuf, "testdocs/crlf.txt");
+        TestBuf.modified = true;
+        size_t end = buf_end(&TestBuf);
+        buf_delete(&TestBuf, end-1, end);
+        CHECK(end-1 == buf_end(&TestBuf));
+        buf_save(&TestBuf);
+        CHECK(end == buf_end(&TestBuf));
+        CHECK(TestBuf.modified == false);
+    }
+
     /* Resizing
      *************************************************************************/
     /* Insertions
@@ -41,6 +227,8 @@ TEST_SUITE(BufferTests) {
         buf_init(&TestBuf, onerror);
         buf_insert(&TestBuf, false, 0, 'a');
         CHECK(buf_text_eq("a"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
     }
 
     TEST(buf_insert should insert at 0) {
@@ -48,6 +236,8 @@ TEST_SUITE(BufferTests) {
         buf_insert(&TestBuf, false, 0, 'b');
         buf_insert(&TestBuf, false, 0, 'a');
         CHECK(buf_text_eq("ab"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
     }
 
     TEST(buf_insert should insert at 1) {
@@ -55,6 +245,8 @@ TEST_SUITE(BufferTests) {
         buf_insert(&TestBuf, false, 0, 'a');
         buf_insert(&TestBuf, false, 1, 'b');
         CHECK(buf_text_eq("ab"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
     }
 
     TEST(buf_insert should insert at 1) {
@@ -63,25 +255,102 @@ TEST_SUITE(BufferTests) {
         buf_insert(&TestBuf, false, 1, 'c');
         buf_insert(&TestBuf, false, 1, 'b');
         CHECK(buf_text_eq("abc"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
     }
 
     TEST(buf_insert should sentence in larger text) {
         set_buffer_text(
-            "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
-        );
-
+            "Lorem ipsum dolor sit amet, consectetur adipiscing elit." );
         buf_insert(&TestBuf, false, 5, ' ');
         buf_insert(&TestBuf, false, 6, 'a');
-
         CHECK(buf_text_eq(
-            "Lorem a ipsum dolor sit amet, consectetur adipiscing elit."
-        ));
+            "Lorem a ipsum dolor sit amet, consectetur adipiscing elit." ));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+    
+    TEST(buf_insert should expand tabs) {
+        set_buffer_text("");
+        TestBuf.expand_tabs = true;
+        buf_insert(&TestBuf, true, 0, '\t');
+        CHECK(buf_text_eq("    "));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+    
+    TEST(buf_insert should copy indent) {
+        set_buffer_text("    ");
+        TestBuf.copy_indent = true;
+        TestBuf.crlf = 0;
+        buf_insert(&TestBuf, true, 4, '\n');
+        CHECK(buf_text_eq("    \n    "));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
     }
 
     /* Deletions
      *************************************************************************/
+    TEST(buf_delete should delete first char) {
+        set_buffer_text("abc");
+        buf_delete(&TestBuf, 0, 1);
+        CHECK(buf_text_eq("bc"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+    
+    TEST(buf_delete should delete second char) {
+        set_buffer_text("abc");
+        buf_delete(&TestBuf, 1, 2);
+        CHECK(buf_text_eq("ac"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+    
+    TEST(buf_delete should delete third char) {
+        set_buffer_text("abc");
+        buf_delete(&TestBuf, 2, 3);
+        CHECK(buf_text_eq("ab"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+
+    TEST(buf_delete should delete more than one char) {
+        set_buffer_text("abcdef");
+        buf_delete(&TestBuf, 1, 5);
+        CHECK(buf_text_eq("af"));
+        CHECK(TestBuf.modified == true);
+        CHECK(TestBuf.redo == NULL);
+    }
+
     /* Undo/Redo
      *************************************************************************/
+    TEST(buf_undo should undo an insert) {
+        Sel sel;
+        set_buffer_text("");
+        buf_insert(&TestBuf, true, 0, 'a');
+        CHECK(buf_text_eq("a"));
+        CHECK(TestBuf.redo == NULL);
+        CHECK(TestBuf.undo != NULL);
+        buf_undo(&TestBuf, &sel);
+        CHECK(buf_text_eq(""));
+        CHECK(TestBuf.redo != NULL);
+        CHECK(TestBuf.undo == NULL);
+    }
+    
+    TEST(buf_undo should undo a delete) {
+        Sel sel;
+        set_buffer_text("a");
+        buf_delete(&TestBuf, 0, 1);
+        CHECK(buf_text_eq(""));
+        CHECK(TestBuf.redo == NULL);
+        CHECK(TestBuf.undo != NULL);
+        buf_undo(&TestBuf, &sel);
+        CHECK(buf_text_eq("a"));
+        CHECK(TestBuf.redo != NULL);
+        CHECK(TestBuf.undo != NULL);
+    }
+
     /* Accessors
      *************************************************************************/
     // buf_get