[pkg-d-commits] [ldc] 31/95: config: Read from string instead of file to aid unittesting

Matthias Klumpp mak at moszumanska.debian.org
Thu Jul 13 20:53:57 UTC 2017


This is an automated email from the git hooks/post-receive script.

mak pushed a commit to annotated tag v1.3.0-beta1
in repository ldc.

commit 8619592bac6fa921360e625ee241b1b5cde15c71
Author: Martin <noone at nowhere.com>
Date:   Sat Feb 25 22:52:05 2017 +0100

    config: Read from string instead of file to aid unittesting
    
    And re-locate the unittests incl. some new ones into their respective
    modules.
---
 driver/config.d     | 172 +++++++++++++++++++++++++++++++++++-----------------
 driver/configfile.d |  14 +++++
 driver/unittests.d  |  89 ---------------------------
 3 files changed, 130 insertions(+), 145 deletions(-)

diff --git a/driver/config.d b/driver/config.d
index ef3fad8..8639f66 100644
--- a/driver/config.d
+++ b/driver/config.d
@@ -99,7 +99,25 @@ class GroupSetting : Setting
 
 Setting[] parseConfigFile(const(char)* filename)
 {
-    auto parser = new Parser(filename);
+    auto dFilename = filename[0 .. strlen(filename)].idup;
+
+    auto file = fopen(filename, "r");
+    if (!file)
+    {
+        throw new Exception(
+            "could not open config file " ~
+            dFilename ~ " for reading");
+    }
+
+    fseek(file, 0, SEEK_END);
+    const fileLength = ftell(file);
+    rewind(file);
+
+    auto content = new char[fileLength];
+    const numRead = fread(content.ptr, 1, fileLength, file);
+    content = content[0 .. numRead];
+
+    auto parser = new Parser(cast(string) content, dFilename);
     return parser.parseConfig();
 }
 
@@ -182,10 +200,11 @@ string humanReadableToken(in Token tok)
 class Parser
 {
     string filename;
-    FILE* file;
-    int lineNum;
+    string content;
+    int index;
+    int lineNum = 1;
 
-    int lastChar = ' ';
+    char lastChar = ' ';
 
     static struct Ahead
     {
@@ -195,17 +214,10 @@ class Parser
     Ahead ahead;
     Ahead* aheadp;
 
-    this(const(char)* filename)
+    this(string content, string filename = "")
     {
-        this.filename = filename[0 .. strlen(filename)].idup;
-        file = fopen(filename, "r");
-        if (!file)
-        {
-            throw new Exception(
-                "could not open config file " ~
-                this.filename ~ " for reading");
-        }
-        this.file = file;
+        this.filename = filename;
+        this.content = content;
     }
 
     void error(in string msg)
@@ -217,18 +229,16 @@ class Parser
         throw new Exception(buf[0 .. len].idup);
     }
 
-    int getChar()
+    char getChar()
     {
-        int c = fgetc(file);
-        if (c == '\n') lineNum += 1;
+        if (index == content.length)
+            return '\0';
+        const c = content[index++];
+        if (c == '\n')
+            ++lineNum;
         return c;
     }
 
-    void ungetChar(int c)
-    {
-        ungetc(c, file);
-    }
-
     Token getTok(out string outStr)
     {
         if (aheadp)
@@ -256,7 +266,7 @@ class Parser
             do
             {
                 lastChar = getChar();
-            } while (lastChar != '\n' && lastChar != EOF);
+            } while (lastChar != '\n' && lastChar != '\0');
 
             return getTok(outStr);
         }
@@ -266,7 +276,7 @@ class Parser
             string name;
             do
             {
-                name ~= cast(char)lastChar;
+                name ~= lastChar;
                 lastChar = getChar();
             }
             while (isalnum(lastChar) || lastChar == '_' || lastChar == '-');
@@ -298,7 +308,7 @@ class Parser
         case ']':
             lastChar = getChar();
             return Token.rbracket;
-        case EOF:
+        case '\0':
             return Token.eof;
         default:
             break;
@@ -317,7 +327,7 @@ class Parser
                     {
                         error("Unexpected end of line in string literal");
                     }
-                    else if (lastChar == EOF)
+                    else if (lastChar == '\0')
                     {
                         error("Unexpected end of file in string literal");
                     }
@@ -339,11 +349,11 @@ class Parser
                             lastChar = '\t';
                             break;
                         default:
-                            error("Unexpected escape sequence: \\"~cast(char)lastChar);
+                            error("Unexpected escape sequence: \\" ~ lastChar);
                             break;
                         }
                     }
-                    str ~= cast(char)lastChar;
+                    str ~= lastChar;
                 }
                 lastChar = getChar();
                 while (isspace(lastChar)) lastChar = getChar();
@@ -353,7 +363,7 @@ class Parser
             return Token.str;
         }
 
-        outStr = [cast(char)lastChar];
+        outStr = [lastChar];
         lastChar = getChar();
         return Token.unknown;
     }
@@ -409,24 +419,7 @@ class Parser
 
         accept(Token.assign);
 
-        string val;
-        string[] arrVal;
-        Setting[] grpVal;
-
-        Setting res;
-
-        final switch(parseValue(val, arrVal, grpVal))
-        {
-        case Setting.Type.scalar:
-            res = new ScalarSetting(name, val);
-            break;
-        case Setting.Type.array:
-            res = new ArraySetting(name, arrVal);
-            break;
-        case Setting.Type.group:
-            res = new GroupSetting(name, grpVal);
-            break;
-        }
+        Setting res = parseValue(name);
 
         string s;
         immutable t = getTok(s);
@@ -438,19 +431,17 @@ class Parser
         return res;
     }
 
-    Setting.Type parseValue(out string val,
-                            out string[] arrVal,
-                            out Setting[] grpVal)
+    Setting parseValue(string name)
     {
         string s;
         auto t = getTok(s);
         if (t == Token.str)
         {
-            val = s;
-            return Setting.Type.scalar;
+            return new ScalarSetting(name, s);
         }
         else if (t == Token.lbracket)
         {
+            string[] arrVal;
             while (1)
             {
                 // get string or rbracket
@@ -461,20 +452,20 @@ class Parser
                     arrVal ~= s;
                     break;
                 case Token.rbracket:
-                    return Setting.Type.array;
+                    return new ArraySetting(name, arrVal);
                 default:
                     unexpectedTokenError(t, Token.str, s);
                     assert(false);
                 }
 
-                // get commar or rbracket
+                // get comma or rbracket
                 t = getTok(s);
                 switch(t)
                 {
                 case Token.comma:
                     break;
                 case Token.rbracket:
-                    return Setting.Type.array;
+                    return new ArraySetting(name, arrVal);;
                 default:
                     unexpectedTokenError(t, Token.comma, s);
                     assert(false);
@@ -483,12 +474,13 @@ class Parser
         }
         else if (t == Token.lbrace)
         {
+            Setting[] grpVal;
             while (1)
             {
                 t = getTok(s);
                 if (t == Token.rbrace)
                 {
-                    return Setting.Type.group;
+                    return new GroupSetting(name, grpVal);
                 }
                 ungetTok(t, s);
                 grpVal ~= parseSetting();
@@ -498,3 +490,71 @@ class Parser
         assert(false);
     }
 }
+
+unittest
+{
+    static void testScalar(string input, string expected)
+    {
+        auto setting = new Parser(input).parseValue(null);
+        assert(setting.type == Setting.Type.scalar);
+        assert((cast(ScalarSetting) setting).val == expected);
+    }
+
+    testScalar(`"abc\r\ndef\t\"quoted/\\123\""`,
+                "abc\r\ndef\t\"quoted/\\123\"");
+    testScalar(`"concatenated" " multiline"
+                " strings"`, "concatenated multiline strings");
+
+    enum input =
+`// comment
+
+// comment
+group-1:
+{
+    // comment
+    scalar = "abc";
+    // comment
+    array_1 = [ "a", "b" ];
+    array_2 = [
+        "c",
+    ];
+};
+// comment
+group-2: { emptyArray = []; };
+`;
+
+    auto settings = new Parser(input).parseConfig();
+    assert(settings.length == 2);
+
+    assert(settings[0].name == "group-1");
+    assert(settings[0].type == Setting.Type.group);
+    auto group1 = cast(GroupSetting) settings[0];
+    assert(group1.children.length == 3);
+
+    assert(group1.children[0].name == "scalar");
+    assert(group1.children[0].type == Setting.Type.scalar);
+    assert((cast(ScalarSetting) group1.children[0]).val == "abc");
+
+    assert(group1.children[1].name == "array_1");
+    assert(group1.children[1].type == Setting.Type.array);
+    auto array1 = cast(ArraySetting) group1.children[1];
+    assert(array1.vals.length == 2);
+    assert(array1.vals[0] == "a");
+    assert(array1.vals[1] == "b");
+
+    assert(group1.children[2].name == "array_2");
+    assert(group1.children[2].type == Setting.Type.array);
+    auto array2 = cast(ArraySetting) group1.children[2];
+    assert(array2.vals.length == 1);
+    assert(array2.vals[0] == "c");
+
+    assert(settings[1].name == "group-2");
+    assert(settings[1].type == Setting.Type.group);
+    auto group2 = cast(GroupSetting) settings[1];
+    assert(group2.children.length == 1);
+
+    assert(group2.children[0].name == "emptyArray");
+    assert(group2.children[0].type == Setting.Type.array);
+    auto emptyArray = cast(ArraySetting) group2.children[0];
+    assert(emptyArray.vals.length == 0);
+}
diff --git a/driver/configfile.d b/driver/configfile.d
index c001c0e..3c1cb70 100644
--- a/driver/configfile.d
+++ b/driver/configfile.d
@@ -73,6 +73,20 @@ string replace(string str, string pattern, string replacement)
     return res;
 }
 
+unittest
+{
+    enum pattern = "pattern";
+    enum test1 = "find the pattern in a sentence";
+    enum test2 = "find the pattern";
+    enum test3 = "pattern in a sentence";
+    enum test4 = "a pattern, yet other patterns";
+
+    assert(replace(test1, pattern, "word") == "find the word in a sentence");
+    assert(replace(test2, pattern, "word") == "find the word");
+    assert(replace(test3, pattern, "word") == "word in a sentence");
+    assert(replace(test4, pattern, "word") == "a word, yet other words");
+}
+
 
 struct ConfigFile
 {
diff --git a/driver/unittests.d b/driver/unittests.d
deleted file mode 100644
index 883b599..0000000
--- a/driver/unittests.d
+++ /dev/null
@@ -1,89 +0,0 @@
-module driver.unittests;
-
-version(unittest):
-
-import driver.config;
-import driver.configfile;
-
-import core.stdc.stdio;
-import core.stdc.string;
-
-
-void writeToFile(const(char)* filepath, const(char)* text)
-{
-    FILE *fp = fopen(filepath, "w");
-    assert(fp, "Cannot open test file for writing: "~filepath[0 .. strlen(filepath)]);
-
-    fputs(text, fp);
-    fclose(fp);
-}
-
-
-// testing driver.configfile.replace
-unittest
-{
-    enum pattern = "pattern";
-    enum test1 = "find the pattern in a sentence";
-    enum test2 = "find the pattern";
-    enum test3 = "pattern in a sentence";
-    enum test4 = "a pattern, yet other patterns";
-
-    assert(replace(test1, pattern, "word") == "find the word in a sentence");
-    assert(replace(test2, pattern, "word") == "find the word");
-    assert(replace(test3, pattern, "word") == "word in a sentence");
-    assert(replace(test4, pattern, "word") == "a word, yet other words");
-}
-
-
-unittest
-{
-    enum confstr =
-`// This configuration file uses libconfig.
-// See http://www.hyperrealm.com/libconfig/ for syntax details.
-// The default group is required
-default:
-{
-    // 'switches' holds array of string that are appends to the command line
-    // arguments before they are parsed.
-    switches = [
-        "-I/opt/dev/ldc/runtime/druntime/src",
-        "-I/opt/dev/ldc/runtime/profile-rt/d",
-        "-I/opt/dev/ldc/runtime/phobos",
-        "-L-L/opt/dev/build/ldc/llvm-3.9.1-Debug/lib",
-        "-defaultlib=phobos2-ldc,druntime-ldc",
-        "-debuglib=phobos2-ldc-debug,druntime-ldc-debug"
-    ];
-    test_cat = "concatenated" " multiline"
-                " strings";
-};
-`;
-
-    enum filename = "ldc_config_test.conf";
-
-    writeToFile(filename, confstr);
-    scope(exit) remove(filename);
-
-    auto settings = parseConfigFile(filename);
-
-    assert(settings.length == 1);
-    assert(settings[0].name == "default");
-    assert(settings[0].type == Setting.Type.group);
-    auto grp = cast(GroupSetting)settings[0];
-    assert(grp.children.length == 2);
-
-    assert(grp.children[0].name == "switches");
-    assert(grp.children[0].type == Setting.Type.array);
-    auto arr = cast(ArraySetting)grp.children[0];
-    assert(arr.vals.length == 6);
-    assert(arr.vals[0] == "-I/opt/dev/ldc/runtime/druntime/src");
-    assert(arr.vals[1] == "-I/opt/dev/ldc/runtime/profile-rt/d");
-    assert(arr.vals[2] == "-I/opt/dev/ldc/runtime/phobos");
-    assert(arr.vals[3] == "-L-L/opt/dev/build/ldc/llvm-3.9.1-Debug/lib");
-    assert(arr.vals[4] == "-defaultlib=phobos2-ldc,druntime-ldc");
-    assert(arr.vals[5] == "-debuglib=phobos2-ldc-debug,druntime-ldc-debug");
-
-    assert(grp.children[1].name == "test_cat");
-    assert(grp.children[1].type == Setting.Type.scalar);
-    auto scalar = cast(ScalarSetting)grp.children[1];
-    assert(scalar.val == "concatenated multiline strings");
-}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-d/ldc.git



More information about the pkg-d-commits mailing list