{"action":"created","comment":{"url":"https://api.github.com/repos/nunit/nunit/pulls/comments/41468276","id":41468276,"diff_hunk":"@@ -0,0 +1,241 @@\n+﻿// ***********************************************************************\n+// Copyright (c) 2015 Charlie Poole\n+//\n+// Permission is hereby granted, free of charge, to any person obtaining\n+// a copy of this software and associated documentation files (the\n+// \"Software\"), to deal in the Software without restriction, including\n+// without limitation the rights to use, copy, modify, merge, publish,\n+// distribute, sublicense, and/or sell copies of the Software, and to\n+// permit persons to whom the Software is furnished to do so, subject to\n+// the following conditions:\n+// \n+// The above copyright notice and this permission notice shall be\n+// included in all copies or substantial portions of the Software.\n+// \n+// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n+// ***********************************************************************\n+\n+using System.Text;\n+using NUnit.Common;\n+\n+namespace NUnit.Engine\n+{\n+    public enum TokenKind\n+    {\n+        Eof,\n+        Word,\n+        String,\n+        Symbol\n+    }\n+\n+    public class Token\n+    {\n+        public Token(TokenKind kind) : this(kind, string.Empty) { }\n+\n+        public Token(TokenKind kind, char ch) : this(kind, ch.ToString()) { }\n+\n+        public Token(TokenKind kind, string text)\n+        {\n+            Kind = kind;\n+            Text = text;\n+        }\n+\n+        public TokenKind Kind { get; private set; }\n+\n+        public string Text { get; private set; }\n+\n+        #region Equality Overrides\n+\n+        public override bool Equals(object obj)\n+        {\n+            return obj is Token && this == (Token)obj;\n+        }\n+\n+        public override int GetHashCode()\n+        {\n+            return Text.GetHashCode();\n+        }\n+\n+        public override string ToString()\n+        {\n+            return Text != null\n+                ? Kind.ToString() + \":\" + Text\n+                : Kind.ToString();\n+        }\n+\n+        public static bool operator ==(Token t1, Token t2)\n+        {\n+            bool t1Null = ReferenceEquals(t1, null);\n+            bool t2Null = ReferenceEquals(t2, null);\n+\n+            if (t1Null && t2Null)\n+                return true;\n+\n+            if (t1Null || t2Null)\n+                return false;\n+\n+            return t1.Kind == t2.Kind && t1.Text == t2.Text;\n+        }\n+\n+        public static bool operator !=(Token t1, Token t2)\n+        {\n+            return !(t1 == t2);\n+        }\n+\n+        #endregion\n+    }\n+\n+    /// <summary>\n+    /// Tokenizer class performs lexical analysis for the TestSelectionParser.\n+    /// It recognizes a very limited set of tokens: words, symbols and\n+    /// quoted strings. This is sufficient for the simple DSL we use to\n+    /// select which tests to run.\n+    /// </summary>\n+    public class Tokenizer\n+    {\n+        private string _input;\n+        private int _index;\n+\n+        private const char EOF = '\\0';\n+        private const string WORD_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.\";","path":"src/NUnitEngine/nunit.engine/Services/Tokenizer.cs","position":106,"original_position":106,"commit_id":"11346ed049db78f6666e51655e927e35cae5bb47","original_commit_id":"b4ad72c3e7d817cd22be082803d8ffccfd5d9fcd","user":{"login":"CharliePoole","id":2678661,"avatar_url":"https://avatars.githubusercontent.com/u/2678661?v=3","gravatar_id":"","url":"https://api.github.com/users/CharliePoole","html_url":"https://github.com/CharliePoole","followers_url":"https://api.github.com/users/CharliePoole/followers","following_url":"https://api.github.com/users/CharliePoole/following{/other_user}","gists_url":"https://api.github.com/users/CharliePoole/gists{/gist_id}","starred_url":"https://api.github.com/users/CharliePoole/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CharliePoole/subscriptions","organizations_url":"https://api.github.com/users/CharliePoole/orgs","repos_url":"https://api.github.com/users/CharliePoole/repos","events_url":"https://api.github.com/users/CharliePoole/events{/privacy}","received_events_url":"https://api.github.com/users/CharliePoole/received_events","type":"User","site_admin":false},"body":"I'm using them for both now. The rhs can generally be either a word or a string. ","created_at":"2015-10-08T01:33:02Z","updated_at":"2015-10-08T01:33:02Z","html_url":"https://github.com/nunit/nunit/pull/877#discussion_r41468276","pull_request_url":"https://api.github.com/repos/nunit/nunit/pulls/877","_links":{"self":{"href":"https://api.github.com/repos/nunit/nunit/pulls/comments/41468276"},"html":{"href":"https://github.com/nunit/nunit/pull/877#discussion_r41468276"},"pull_request":{"href":"https://api.github.com/repos/nunit/nunit/pulls/877"}}},"pull_request":{"url":"https://api.github.com/repos/nunit/nunit/pulls/877","id":47094488,"html_url":"https://github.com/nunit/nunit/pull/877","diff_url":"https://github.com/nunit/nunit/pull/877.diff","patch_url":"https://github.com/nunit/nunit/pull/877.patch","issue_url":"https://api.github.com/repos/nunit/nunit/issues/877","number":877,"state":"open","locked":false,"title":"Initial commit for new approach to test selection","user":{"login":"CharliePoole","id":2678661,"avatar_url":"https://avatars.githubusercontent.com/u/2678661?v=3","gravatar_id":"","url":"https://api.github.com/users/CharliePoole","html_url":"https://github.com/CharliePoole","followers_url":"https://api.github.com/users/CharliePoole/followers","following_url":"https://api.github.com/users/CharliePoole/following{/other_user}","gists_url":"https://api.github.com/users/CharliePoole/gists{/gist_id}","starred_url":"https://api.github.com/users/CharliePoole/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CharliePoole/subscriptions","organizations_url":"https://api.github.com/users/CharliePoole/orgs","repos_url":"https://api.github.com/users/CharliePoole/repos","events_url":"https://api.github.com/users/CharliePoole/events{/privacy}","received_events_url":"https://api.github.com/users/CharliePoole/received_events","type":"User","site_admin":false},"body":"**Do Not Merge Yet**\r\n\r\nThis is my first cut at resolving #374. Here's what's done in the initial commit:\r\n\r\n* NUnit-console has a new option --where, which takes a string (usually quoted) that describes what to select.\r\n\r\n* The console display has been modified to show all test filtering including the list of tests provided by --test or --testlist, categories provided through --include or --exclude and the where clause itself.\r\n\r\n* The creation of a test filter is done by the TestFilterService of the engine. This is a new service. It incorporates, in modified  form, the TestFixtureBuilder class previously in the console project and a parser for the selection language.\r\n\r\n* New filter types have been created for selection of tests by Name, MethodName and ClassName. (We previously only filtered on the FullName of the test). I verified manually that the new filters work but I have not yet added tests - they are actually just copies of an existing filter with the field to examine being the only thing changed.\r\n\r\n* The operators == and != are fully implemented - that is, they create a filter, which is passed to the framework and does the right thing. The =~ and !~ operators are parsed correctly but don't yet create filters that work.\r\n\r\n* The lhs comparison keywords category, cat, test, fullname, name, method, class and id all work, but the results may be unexpected in some cases. For example, selecting by class name causes all explicit tests to be executed. Details like this still have to be worked out.\r\n\r\n* The rhs of each comparison may usually be a \"word\" or a string. Strings are delimited with double quotes, single quotes or slashes. We may add others. Backslash allows escaping the quote char so it may be included. You use \\\\ to include a backslash.\r\n\r\n* Combination of comparisons with && and || (or & and |) is supported. Normal precedence is observed and may be changed using parentheses. A ! symbol may precede any parenthesized expression, negating it.\r\n\r\nRemaining to be done at this time:\r\n\r\n* More thorough testing, particularly all the way through to the framework.\r\n\r\n* Addition of property names in the comparisons.\r\n\r\n* Some key decisions about how this is implemented and where to take it - see below.\r\n\r\nReview needed:\r\n\r\n* My code, obviously.\r\n\r\n* The optioins supported on the console. It seems to me we have no choice but to support --testlist for people who generate lists of tests to be run. We could decide to remove --test, --include and --exclude, since everything they do is doable with the selection language.\r\n\r\n* Review and finalize the syntax. Possibly think up a name for this language. NTSL?\r\n\r\n* Review my decision to put it in the engine. I think it makes sense that each runner not have to create this themselves. I could have just made filters simply contain the language itself but the use of a builder allows a bit more flexibility.\r\n","created_at":"2015-10-07T23:14:05Z","updated_at":"2015-10-08T01:33:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"597cadf1116ea7b237e0ea82a8aa1589854ca5fb","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nunit/nunit/pulls/877/commits","review_comments_url":"https://api.github.com/repos/nunit/nunit/pulls/877/comments","review_comment_url":"https://api.github.com/repos/nunit/nunit/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nunit/nunit/issues/877/comments","statuses_url":"https://api.github.com/repos/nunit/nunit/statuses/11346ed049db78f6666e51655e927e35cae5bb47","head":{"label":"nunit:issue-374","ref":"issue-374","sha":"11346ed049db78f6666e51655e927e35cae5bb47","user":{"login":"nunit","id":2678858,"avatar_url":"https://avatars.githubusercontent.com/u/2678858?v=3","gravatar_id":"","url":"https://api.github.com/users/nunit","html_url":"https://github.com/nunit","followers_url":"https://api.github.com/users/nunit/followers","following_url":"https://api.github.com/users/nunit/following{/other_user}","gists_url":"https://api.github.com/users/nunit/gists{/gist_id}","starred_url":"https://api.github.com/users/nunit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nunit/subscriptions","organizations_url":"https://api.github.com/users/nunit/orgs","repos_url":"https://api.github.com/users/nunit/repos","events_url":"https://api.github.com/users/nunit/events{/privacy}","received_events_url":"https://api.github.com/users/nunit/received_events","type":"Organization","site_admin":false},"repo":{"id":13692183,"name":"nunit","full_name":"nunit/nunit","owner":{"login":"nunit","id":2678858,"avatar_url":"https://avatars.githubusercontent.com/u/2678858?v=3","gravatar_id":"","url":"https://api.github.com/users/nunit","html_url":"https://github.com/nunit","followers_url":"https://api.github.com/users/nunit/followers","following_url":"https://api.github.com/users/nunit/following{/other_user}","gists_url":"https://api.github.com/users/nunit/gists{/gist_id}","starred_url":"https://api.github.com/users/nunit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nunit/subscriptions","organizations_url":"https://api.github.com/users/nunit/orgs","repos_url":"https://api.github.com/users/nunit/repos","events_url":"https://api.github.com/users/nunit/events{/privacy}","received_events_url":"https://api.github.com/users/nunit/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nunit/nunit","description":"NUnit 3.0 framework, engine and console runner","fork":false,"url":"https://api.github.com/repos/nunit/nunit","forks_url":"https://api.github.com/repos/nunit/nunit/forks","keys_url":"https://api.github.com/repos/nunit/nunit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nunit/nunit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nunit/nunit/teams","hooks_url":"https://api.github.com/repos/nunit/nunit/hooks","issue_events_url":"https://api.github.com/repos/nunit/nunit/issues/events{/number}","events_url":"https://api.github.com/repos/nunit/nunit/events","assignees_url":"https://api.github.com/repos/nunit/nunit/assignees{/user}","branches_url":"https://api.github.com/repos/nunit/nunit/branches{/branch}","tags_url":"https://api.github.com/repos/nunit/nunit/tags","blobs_url":"https://api.github.com/repos/nunit/nunit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nunit/nunit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nunit/nunit/git/refs{/sha}","trees_url":"https://api.github.com/repos/nunit/nunit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nunit/nunit/statuses/{sha}","languages_url":"https://api.github.com/repos/nunit/nunit/languages","stargazers_url":"https://api.github.com/repos/nunit/nunit/stargazers","contributors_url":"https://api.github.com/repos/nunit/nunit/contributors","subscribers_url":"https://api.github.com/repos/nunit/nunit/subscribers","subscription_url":"https://api.github.com/repos/nunit/nunit/subscription","commits_url":"https://api.github.com/repos/nunit/nunit/commits{/sha}","git_commits_url":"https://api.github.com/repos/nunit/nunit/git/commits{/sha}","comments_url":"https://api.github.com/repos/nunit/nunit/comments{/number}","issue_comment_url":"https://api.github.com/repos/nunit/nunit/issues/comments{/number}","contents_url":"https://api.github.com/repos/nunit/nunit/contents/{+path}","compare_url":"https://api.github.com/repos/nunit/nunit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nunit/nunit/merges","archive_url":"https://api.github.com/repos/nunit/nunit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nunit/nunit/downloads","issues_url":"https://api.github.com/repos/nunit/nunit/issues{/number}","pulls_url":"https://api.github.com/repos/nunit/nunit/pulls{/number}","milestones_url":"https://api.github.com/repos/nunit/nunit/milestones{/number}","notifications_url":"https://api.github.com/repos/nunit/nunit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nunit/nunit/labels{/name}","releases_url":"https://api.github.com/repos/nunit/nunit/releases{/id}","created_at":"2013-10-18T23:43:29Z","updated_at":"2015-10-05T14:14:14Z","pushed_at":"2015-10-08T00:39:10Z","git_url":"git://github.com/nunit/nunit.git","ssh_url":"git@github.com:nunit/nunit.git","clone_url":"https://github.com/nunit/nunit.git","svn_url":"https://github.com/nunit/nunit","homepage":"http://www.nunit.org/","size":41696,"stargazers_count":353,"watchers_count":353,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":161,"mirror_url":null,"open_issues_count":141,"forks":161,"open_issues":141,"watchers":353,"default_branch":"master"}},"base":{"label":"nunit:master","ref":"master","sha":"7df9de72a8d25df6331f7b5abc4005d6d9a7f324","user":{"login":"nunit","id":2678858,"avatar_url":"https://avatars.githubusercontent.com/u/2678858?v=3","gravatar_id":"","url":"https://api.github.com/users/nunit","html_url":"https://github.com/nunit","followers_url":"https://api.github.com/users/nunit/followers","following_url":"https://api.github.com/users/nunit/following{/other_user}","gists_url":"https://api.github.com/users/nunit/gists{/gist_id}","starred_url":"https://api.github.com/users/nunit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nunit/subscriptions","organizations_url":"https://api.github.com/users/nunit/orgs","repos_url":"https://api.github.com/users/nunit/repos","events_url":"https://api.github.com/users/nunit/events{/privacy}","received_events_url":"https://api.github.com/users/nunit/received_events","type":"Organization","site_admin":false},"repo":{"id":13692183,"name":"nunit","full_name":"nunit/nunit","owner":{"login":"nunit","id":2678858,"avatar_url":"https://avatars.githubusercontent.com/u/2678858?v=3","gravatar_id":"","url":"https://api.github.com/users/nunit","html_url":"https://github.com/nunit","followers_url":"https://api.github.com/users/nunit/followers","following_url":"https://api.github.com/users/nunit/following{/other_user}","gists_url":"https://api.github.com/users/nunit/gists{/gist_id}","starred_url":"https://api.github.com/users/nunit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nunit/subscriptions","organizations_url":"https://api.github.com/users/nunit/orgs","repos_url":"https://api.github.com/users/nunit/repos","events_url":"https://api.github.com/users/nunit/events{/privacy}","received_events_url":"https://api.github.com/users/nunit/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nunit/nunit","description":"NUnit 3.0 framework, engine and console runner","fork":false,"url":"https://api.github.com/repos/nunit/nunit","forks_url":"https://api.github.com/repos/nunit/nunit/forks","keys_url":"https://api.github.com/repos/nunit/nunit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nunit/nunit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nunit/nunit/teams","hooks_url":"https://api.github.com/repos/nunit/nunit/hooks","issue_events_url":"https://api.github.com/repos/nunit/nunit/issues/events{/number}","events_url":"https://api.github.com/repos/nunit/nunit/events","assignees_url":"https://api.github.com/repos/nunit/nunit/assignees{/user}","branches_url":"https://api.github.com/repos/nunit/nunit/branches{/branch}","tags_url":"https://api.github.com/repos/nunit/nunit/tags","blobs_url":"https://api.github.com/repos/nunit/nunit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nunit/nunit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nunit/nunit/git/refs{/sha}","trees_url":"https://api.github.com/repos/nunit/nunit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nunit/nunit/statuses/{sha}","languages_url":"https://api.github.com/repos/nunit/nunit/languages","stargazers_url":"https://api.github.com/repos/nunit/nunit/stargazers","contributors_url":"https://api.github.com/repos/nunit/nunit/contributors","subscribers_url":"https://api.github.com/repos/nunit/nunit/subscribers","subscription_url":"https://api.github.com/repos/nunit/nunit/subscription","commits_url":"https://api.github.com/repos/nunit/nunit/commits{/sha}","git_commits_url":"https://api.github.com/repos/nunit/nunit/git/commits{/sha}","comments_url":"https://api.github.com/repos/nunit/nunit/comments{/number}","issue_comment_url":"https://api.github.com/repos/nunit/nunit/issues/comments{/number}","contents_url":"https://api.github.com/repos/nunit/nunit/contents/{+path}","compare_url":"https://api.github.com/repos/nunit/nunit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nunit/nunit/merges","archive_url":"https://api.github.com/repos/nunit/nunit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nunit/nunit/downloads","issues_url":"https://api.github.com/repos/nunit/nunit/issues{/number}","pulls_url":"https://api.github.com/repos/nunit/nunit/pulls{/number}","milestones_url":"https://api.github.com/repos/nunit/nunit/milestones{/number}","notifications_url":"https://api.github.com/repos/nunit/nunit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nunit/nunit/labels{/name}","releases_url":"https://api.github.com/repos/nunit/nunit/releases{/id}","created_at":"2013-10-18T23:43:29Z","updated_at":"2015-10-05T14:14:14Z","pushed_at":"2015-10-08T00:39:10Z","git_url":"git://github.com/nunit/nunit.git","ssh_url":"git@github.com:nunit/nunit.git","clone_url":"https://github.com/nunit/nunit.git","svn_url":"https://github.com/nunit/nunit","homepage":"http://www.nunit.org/","size":41696,"stargazers_count":353,"watchers_count":353,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":161,"mirror_url":null,"open_issues_count":141,"forks":161,"open_issues":141,"watchers":353,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nunit/nunit/pulls/877"},"html":{"href":"https://github.com/nunit/nunit/pull/877"},"issue":{"href":"https://api.github.com/repos/nunit/nunit/issues/877"},"comments":{"href":"https://api.github.com/repos/nunit/nunit/issues/877/comments"},"review_comments":{"href":"https://api.github.com/repos/nunit/nunit/pulls/877/comments"},"review_comment":{"href":"https://api.github.com/repos/nunit/nunit/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nunit/nunit/pulls/877/commits"},"statuses":{"href":"https://api.github.com/repos/nunit/nunit/statuses/11346ed049db78f6666e51655e927e35cae5bb47"}}}}