66using System ;
77using System . Collections . Generic ;
88using System . IO ;
9+ using System . Linq ;
910using System . Text . RegularExpressions ;
1011using Microsoft . VisualStudio . TestTools . UnitTesting ;
1112using Mono . Cecil ;
@@ -60,6 +61,8 @@ public class StubsGenerationTests
6061 "static void NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr );" ;
6162
6263 private string _stubsPath ;
64+ private List < string > _nfTestLibTypeToIncludeInLookupTable = new List < string > ( ) ;
65+ private List < string > _nfTestLibTypeToIncludeInHeader = new List < string > ( ) ;
6366
6467 [ TestMethod ]
6568 public void GeneratingStubsFromNFAppTest ( )
@@ -212,6 +215,49 @@ public void BackingFieldsAbsentTests()
212215 Assert . IsFalse ( Regex . IsMatch ( generatedAssemblyHeaderFile , @"(?<!')<\w+>k__BackingField(?!')" ) , "Found a name with BackingField pattern, when it shouldn't" ) ;
213216 }
214217
218+ [ TestMethod ]
219+ public void StubsAndDeclarationMatchTests ( )
220+ {
221+ string generatedAssemblyHeaderFile = File . ReadAllText ( $ "{ _stubsPath } \\ TestNFClassLibrary.h") ;
222+ string generatedAssemblyLookupFile = File . ReadAllText ( $ "{ _stubsPath } \\ TestNFClassLibrary.cpp") ;
223+
224+ // extract all type definitions from the header file
225+ MatchCollection typeDefinitionsInHeader = Regex . Matches ( generatedAssemblyHeaderFile , @"struct\s{1}(\w+_\w+_\w+_\w+)\b" , RegexOptions . IgnoreCase | RegexOptions . Multiline ) ;
226+
227+ // extract all type definitions from the lookup file
228+ List < Match > typeDefinitionsInLookupTable = Regex . Matches ( generatedAssemblyLookupFile , @"^\s{4}(\w+_\w+_\w+_\w+)::" , RegexOptions . IgnoreCase | RegexOptions . Multiline )
229+ . Cast < Match > ( )
230+ . GroupBy ( m => m . Groups [ 1 ] . Value )
231+ . Select ( g => g . First ( ) )
232+ . ToList ( ) ;
233+
234+ // check if all entries in lookup table are present in the header
235+ foreach ( Match typeDefinition in typeDefinitionsInLookupTable )
236+ {
237+ string typeName = typeDefinition . Groups [ 1 ] . Value ;
238+ bool found = typeDefinitionsInHeader . Cast < Match > ( ) . Any ( md => md . Groups [ 1 ] . Value == typeName ) ;
239+ Assert . IsTrue ( found , $ "Type definition { typeName } not found in header file") ;
240+ }
241+
242+ // check if all expected types are present in the lookup table
243+ Assert . AreEqual ( _nfTestLibTypeToIncludeInLookupTable . Count , typeDefinitionsInLookupTable . Count , "Number of type definitions don't match" ) ;
244+
245+ foreach ( string typeName in _nfTestLibTypeToIncludeInLookupTable )
246+ {
247+ bool found = typeDefinitionsInLookupTable . Any ( md => md . Groups [ 1 ] . Value == typeName ) ;
248+ Assert . IsTrue ( found , $ "Type definition { typeName } not found in lookup table") ;
249+ }
250+
251+ // check if all expected types are present in the header file
252+ Assert . AreEqual ( _nfTestLibTypeToIncludeInHeader . Count , typeDefinitionsInHeader . Count , "Number of type definitions don't match" ) ;
253+
254+ foreach ( string typeName in _nfTestLibTypeToIncludeInHeader )
255+ {
256+ bool found = typeDefinitionsInHeader . Cast < Match > ( ) . Any ( md => md . Groups [ 1 ] . Value == typeName ) ;
257+ Assert . IsTrue ( found , $ "Type definition { typeName } not found in header file") ;
258+ }
259+ }
260+
215261 [ TestInitialize ]
216262 public void GenerateStubs ( )
217263 {
@@ -256,6 +302,16 @@ public void GenerateStubs()
256302
257303 assemblyBuilder . Minimize ( ) ;
258304
305+ // recompile
306+ using ( FileStream stream = File . Open (
307+ Path . ChangeExtension ( stubsGenerationFileToCompile , "tmp" ) ,
308+ FileMode . Create ,
309+ FileAccess . ReadWrite ) )
310+ using ( BinaryWriter writer = new BinaryWriter ( stream ) )
311+ {
312+ assemblyBuilder . Write ( GetBinaryWriter ( writer ) ) ;
313+ }
314+
259315 nanoTablesContext tablesContext = assemblyBuilder . TablesContext ;
260316
261317 var skeletonGenerator = new nanoSkeletonGenerator (
@@ -296,6 +352,17 @@ public void GenerateStubs()
296352
297353 assemblyBuilder . Minimize ( ) ;
298354
355+ // recompile
356+ using ( FileStream stream = File . Open (
357+ Path . ChangeExtension ( nfLibFileToCompile , "tmp" ) ,
358+ FileMode . Create ,
359+ FileAccess . ReadWrite ) )
360+
361+ using ( BinaryWriter writer = new BinaryWriter ( stream ) )
362+ {
363+ assemblyBuilder . Write ( GetBinaryWriter ( writer ) ) ;
364+ }
365+
299366 tablesContext = assemblyBuilder . TablesContext ;
300367
301368 skeletonGenerator = new nanoSkeletonGenerator (
@@ -307,6 +374,39 @@ public void GenerateStubs()
307374 true ) ;
308375
309376 skeletonGenerator . GenerateSkeleton ( ) ;
377+
378+ // save types that are to be included from assembly lookup declaration
379+ foreach ( TypeDefinition c in tablesContext . TypeDefinitionTable . Items )
380+ {
381+ if ( c . HasMethods && nanoSkeletonGenerator . ShouldIncludeType ( c ) )
382+ {
383+ foreach ( MethodDefinition m in nanoTablesContext . GetOrderedMethods ( c . Methods ) )
384+ {
385+ ushort rva = tablesContext . ByteCodeTable . GetMethodRva ( m ) ;
386+
387+ // check method inclusion
388+ // method is not a native implementation (RVA 0xFFFF) and is not abstract
389+ if ( rva == 0xFFFF && ! m . IsAbstract )
390+ {
391+ _nfTestLibTypeToIncludeInLookupTable . Add ( $ "Library_{ skeletonGenerator . SafeProjectName } _{ NativeMethodsCrc . GetClassName ( c ) } ") ;
392+
393+ // only need to add the type once
394+ break ;
395+ }
396+ }
397+ }
398+ }
399+
400+ // save types that are to be included in assembly header
401+ foreach ( TypeDefinition c in tablesContext . TypeDefinitionTable . Items )
402+ {
403+ if ( nanoSkeletonGenerator . ShouldIncludeType ( c )
404+ && c . HasMethods
405+ && c . HasFields )
406+ {
407+ _nfTestLibTypeToIncludeInHeader . Add ( $ "Library_{ skeletonGenerator . SafeProjectName } _{ NativeMethodsCrc . GetClassName ( c ) } ") ;
408+ }
409+ }
310410 }
311411
312412 [ TestCleanup ]
0 commit comments