2016-12-14 9 views
6

पृष्ठभूमिStackExchange.Precompilation - मैं प्रीकंपिलेशन डायग्नोस्टिक्स का परीक्षण कैसे कर सकता हूं?

मैं सी # में पहलू-ओरिएंटेड प्रोग्रामिंग लागू करने के लिए StackExchange.Precompilation उपयोग कर रहा हूँ। See my repository on GitHub.

मूल विचार यह है कि क्लाइंट कोड सदस्यों पर कस्टम विशेषताओं को रखने में सक्षम होगा, और प्रीकंपलर उन विशेषताओं वाले किसी भी सदस्यों पर वाक्यविन्यास परिवर्तन करेगा। एक सरल उदाहरण NonNullAttribute मैंने बनाया है। NonNullAttribute एक पैरामीटर p पर रखा जाता है, precompiler विधि शरीर की शुरुआत में

if (Object.Equals(p, null)) throw new ArgumentNullException(nameof(p)); 

डाल देंगे।


निदान भयानक हैं ...

मैं यह मुश्किल इन विशेषताओं को गलत तरीके से उपयोग करने के लिए करना चाहते हैं। मुझे सबसे अच्छा तरीका मिला है (अंतर्ज्ञानी डिज़ाइन से अलग) गुणों के अमान्य या अजीब उपयोगों के लिए संकलन-समय Diagnostic एस बनाना है।

उदाहरण के लिए, NonNullAttribute मूल्य-टाइप किए गए सदस्यों पर उपयोग करने के लिए समझ में नहीं आता है। (यहां तक ​​कि शून्य मूल्यवान मानों के लिए, क्योंकि यदि आप गारंटी देना चाहते हैं कि वे शून्य नहीं हैं तो इसके बजाय एक गैर-शून्य प्रकार का उपयोग किया जाना चाहिए।) Diagnostic बनाना इस त्रुटि के उपयोगकर्ता को सूचित करने का एक शानदार तरीका है, बिना निर्माण को दुर्घटनाग्रस्त किए एक अपवाद की तरह।


... लेकिन मैं उनका परीक्षण कैसे करूं?

डायग्नोस्टिक्स त्रुटियों को हाइलाइट करने का एक शानदार तरीका है, लेकिन मैं यह भी सुनिश्चित करना चाहता हूं कि मेरे डायग्नोस्टिक बनाने कोड में त्रुटियां नहीं हैं। मैं एक इकाई परीक्षण है कि इस

public class TestClass { 
    public void ShouldCreateDiagnostic([NonNull] int n) { }  
} 

की तरह एक कोड नमूना precompile और पुष्टि करें कि सही निदान बनाई गई है (या कुछ मामलों में है कि कोई निदान नहीं बनाई गई है) कर सकते हैं स्थापित करने के लिए सक्षम होने के लिए करना चाहते हैं।

क्या कोई भी स्टैक एक्सचेंज से परिचित हो सकता है। प्रीकंपिलेशन मुझे इस पर कुछ मार्गदर्शन देता है?


समाधान:

जवाब @ m0sa द्वारा दिए गए अविश्वसनीय रूप से मददगार था। कार्यान्वयन के लिए बहुत सारे विवरण हैं, इसलिए यहां यूनिट टेस्ट वास्तव में दिखता है (NUnit 3 का उपयोग करके)। using staticSyntaxFactory के लिए नोट करें, यह वाक्यविन्यास वृक्ष निर्माण में बहुत अधिक अव्यवस्था को हटा देता है।

using System.Collections.Generic; 
using System.Linq; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using NUnit.Framework; 
using StackExchange.Precompilation; 
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 

namespace MyPrecompiler.Tests { 

    [TestFixture] 
    public class NonNull_CompilationDiagnosticsTest { 

     [Test] 
     public void NonNullAttribute_CreatesDiagnosticIfAppliedToValueTypeParameter() { 

      var context = new BeforeCompileContext { 
       Compilation = TestCompilation_NonNullOnValueTypeParameter(), 
       Diagnostics = new List<Diagnostic>() 
      }; 

      ICompileModule module = new MyPrecompiler.MyModule(); 

      module.BeforeCompile(context); 

      var diagnostic = context.Diagnostics.SingleOrDefault(); 

      Assert.NotNull(diagnostic); 
      Assert.AreEqual("MyPrecompiler: Invalid attribute usage", 
          diagnostic.Descriptor.Title.ToString()); //Must use ToString() because Title is a LocalizeableString 
     } 

     //Make sure there are spaces before the member name, parameter names, and parameter types. 
     private CSharpCompilation TestCompilation_NonNullOnValueTypeParameter() { 
      return CreateCompilation(
       MethodDeclaration(ParseTypeName("void"), Identifier(" TestMethod")) 
       .AddParameterListParameters(
        Parameter(Identifier(" param1")) 
         .WithType(ParseTypeName(" int")) 
         .AddAttributeLists(AttributeList() 
              .AddAttributes(Attribute(ParseName("NonNull")))))); 
     } 

     //Make sure to include Using directives 
     private CSharpCompilation CreateCompilation(params MemberDeclarationSyntax[] members) { 

      return CSharpCompilation.Create("TestAssembly") 
       .AddReferences(References) 
       .AddSyntaxTrees(CSharpSyntaxTree.Create(CompilationUnit() 
        .AddUsings(UsingDirective(ParseName(" Traction"))) 
        .AddMembers(ClassDeclaration(Identifier(" TestClass")) 
           .AddMembers(members)))); 
     } 

     private string runtimePath = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\"; 

     private MetadataReference[] References => 
      new[] { 
       MetadataReference.CreateFromFile(runtimePath + "mscorlib.dll"), 
       MetadataReference.CreateFromFile(runtimePath + "System.dll"), 
       MetadataReference.CreateFromFile(runtimePath + "System.Core.dll"), 
       MetadataReference.CreateFromFile(typeof(NonNullAttribute).Assembly.Location) 
      }; 
    } 
} 

उत्तर

2

मैं समझ आप वास्तविक फेंकना/संकलन इससे पहले कि आप निदान जोड़ना चाहते हैं, तो चरणों होगा:

  1. अपने CSharpCompilation बनाने के लिए,
  2. आगे जाने से पहले सुनिश्चित करें कि यह कोई नैदानिक ​​त्रुटियाँ हैं बनाने
  3. BeforeCompileContext बनाएं, और संकलन के साथ इसे खाली करें और खाली List<Diagnostic>
  4. अपने ICompileModule का एक उदाहरण बनाएं और संदर्भ के साथ ICompileModule.BeforeCompile पर कॉल करें रॉम चरण 2
  5. जांच इसमें मौजूद आवश्यक Diagnostic
+0

बहुत बहुत शुक्रिया। मैं इसे अभी कोशिश कर रहा हूं और वापस रिपोर्ट करूंगा। – JamesFaix

संबंधित मुद्दे