1## @file
2# This file contain unit test for DecParser
3#
4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5#
6# SPDX-License-Identifier: BSD-2-Clause-Patent
7
8import os
9import unittest
10from Logger.Log import FatalError
11
12from Parser.DecParser import \
13    Dec, \
14    _DecDefine, \
15    _DecLibraryclass, \
16    _DecPcd, \
17    _DecGuid, \
18    FileContent, \
19    _DecBase, \
20    CleanString
21
22from Object.Parser.DecObject import _DecComments
23
24#
25# Test CleanString
26#
27class CleanStringTestCase(unittest.TestCase):
28    def testCleanString(self):
29        Line, Comment = CleanString('')
30        self.assertEqual(Line, '')
31        self.assertEqual(Comment, '')
32
33        Line, Comment = CleanString('line without comment')
34        self.assertEqual(Line, 'line without comment')
35        self.assertEqual(Comment, '')
36
37        Line, Comment = CleanString('# pure comment')
38        self.assertEqual(Line, '')
39        self.assertEqual(Comment, '# pure comment')
40
41        Line, Comment = CleanString('line # and comment')
42        self.assertEqual(Line, 'line')
43        self.assertEqual(Comment, '# and comment')
44
45    def testCleanStringCpp(self):
46        Line, Comment = CleanString('line // and comment', AllowCppStyleComment = True)
47        self.assertEqual(Line, 'line')
48        self.assertEqual(Comment, '# and comment')
49
50#
51# Test _DecBase._MacroParser function
52#
53class MacroParserTestCase(unittest.TestCase):
54    def setUp(self):
55        self.dec = _DecBase(FileContent('dummy', []))
56
57    def testCorrectMacro(self):
58        self.dec._MacroParser('DEFINE MACRO1 = test1')
59        self.failIf('MACRO1' not in self.dec._LocalMacro)
60        self.assertEqual(self.dec._LocalMacro['MACRO1'], 'test1')
61
62    def testErrorMacro1(self):
63        # Raise fatal error, macro name must be upper case letter
64        self.assertRaises(FatalError, self.dec._MacroParser, 'DEFINE not_upper_case = test2')
65
66    def testErrorMacro2(self):
67        # No macro name given
68        self.assertRaises(FatalError, self.dec._MacroParser, 'DEFINE ')
69
70#
71# Test _DecBase._TryBackSlash function
72#
73class TryBackSlashTestCase(unittest.TestCase):
74    def setUp(self):
75        Content = [
76            # Right case
77            'test no backslash',
78
79            'test with backslash \\',
80            'continue second line',
81
82            # Do not precede with whitespace
83            '\\',
84
85            # Empty line after backlash is not allowed
86            'line with backslash \\',
87            ''
88        ]
89        self.dec = _DecBase(FileContent('dummy', Content))
90
91    def testBackSlash(self):
92        #
93        # Right case, assert return values
94        #
95        ConcatLine, CommentList = self.dec._TryBackSlash(self.dec._RawData.GetNextLine(), [])
96        self.assertEqual(ConcatLine, 'test no backslash')
97        self.assertEqual(CommentList, [])
98
99        ConcatLine, CommentList = self.dec._TryBackSlash(self.dec._RawData.GetNextLine(), [])
100        self.assertEqual(CommentList, [])
101        self.assertEqual(ConcatLine, 'test with backslash continue second line')
102
103        #
104        # Error cases, assert raise exception
105        #
106        self.assertRaises(FatalError, self.dec._TryBackSlash, self.dec._RawData.GetNextLine(), [])
107        self.assertRaises(FatalError, self.dec._TryBackSlash, self.dec._RawData.GetNextLine(), [])
108
109#
110# Test _DecBase.Parse function
111#
112class DataItem(_DecComments):
113    def __init__(self):
114        _DecComments.__init__(self)
115        self.String = ''
116
117class Data(_DecComments):
118    def __init__(self):
119        _DecComments.__init__(self)
120        # List of DataItem
121        self.ItemList = []
122
123class TestInner(_DecBase):
124    def __init__(self, RawData):
125        _DecBase.__init__(self, RawData)
126        self.ItemObject = Data()
127
128    def _StopCurrentParsing(self, Line):
129        return Line == '[TOP]'
130
131    def _ParseItem(self):
132        Item = DataItem()
133        Item.String = self._RawData.CurrentLine
134        self.ItemObject.ItemList.append(Item)
135        return Item
136
137    def _TailCommentStrategy(self, Comment):
138        return Comment.find('@comment') != -1
139
140class TestTop(_DecBase):
141    def __init__(self, RawData):
142        _DecBase.__init__(self, RawData)
143        # List of Data
144        self.ItemObject = []
145
146    # Top parser
147    def _StopCurrentParsing(self, Line):
148        return False
149
150    def _ParseItem(self):
151        TestParser = TestInner(self._RawData)
152        TestParser.Parse()
153        self.ItemObject.append(TestParser.ItemObject)
154        return TestParser.ItemObject
155
156class ParseTestCase(unittest.TestCase):
157    def setUp(self):
158        pass
159
160    def testParse(self):
161        Content = \
162        '''# Top comment
163        [TOP]
164          # sub1 head comment
165          (test item has both head and tail comment) # sub1 tail comment
166          # sub2 head comment
167          (test item has head and special tail comment)
168          # @comment test TailCommentStrategy branch
169
170          (test item has no comment)
171
172        # test NextLine branch
173        [TOP]
174          sub-item
175        '''
176        dec = TestTop(FileContent('dummy', Content.splitlines()))
177        dec.Parse()
178
179        # Two sections
180        self.assertEqual(len(dec.ItemObject), 2)
181
182        data = dec.ItemObject[0]
183        self.assertEqual(data._HeadComment[0][0], '# Top comment')
184        self.assertEqual(data._HeadComment[0][1], 1)
185
186        # 3 subitems
187        self.assertEqual(len(data.ItemList), 3)
188
189        dataitem = data.ItemList[0]
190        self.assertEqual(dataitem.String, '(test item has both head and tail comment)')
191        # Comment content
192        self.assertEqual(dataitem._HeadComment[0][0], '# sub1 head comment')
193        self.assertEqual(dataitem._TailComment[0][0], '# sub1 tail comment')
194        # Comment line number
195        self.assertEqual(dataitem._HeadComment[0][1], 3)
196        self.assertEqual(dataitem._TailComment[0][1], 4)
197
198        dataitem = data.ItemList[1]
199        self.assertEqual(dataitem.String, '(test item has head and special tail comment)')
200        # Comment content
201        self.assertEqual(dataitem._HeadComment[0][0], '# sub2 head comment')
202        self.assertEqual(dataitem._TailComment[0][0], '# @comment test TailCommentStrategy branch')
203        # Comment line number
204        self.assertEqual(dataitem._HeadComment[0][1], 5)
205        self.assertEqual(dataitem._TailComment[0][1], 7)
206
207        dataitem = data.ItemList[2]
208        self.assertEqual(dataitem.String, '(test item has no comment)')
209        # Comment content
210        self.assertEqual(dataitem._HeadComment, [])
211        self.assertEqual(dataitem._TailComment, [])
212
213        data = dec.ItemObject[1]
214        self.assertEqual(data._HeadComment[0][0], '# test NextLine branch')
215        self.assertEqual(data._HeadComment[0][1], 11)
216
217        # 1 subitems
218        self.assertEqual(len(data.ItemList), 1)
219
220        dataitem = data.ItemList[0]
221        self.assertEqual(dataitem.String, 'sub-item')
222        self.assertEqual(dataitem._HeadComment, [])
223        self.assertEqual(dataitem._TailComment, [])
224
225#
226# Test _DecDefine._ParseItem
227#
228class DecDefineTestCase(unittest.TestCase):
229    def GetObj(self, Content):
230        Obj = _DecDefine(FileContent('dummy', Content.splitlines()))
231        Obj._RawData.CurrentLine = Obj._RawData.GetNextLine()
232        return Obj
233
234    def testDecDefine(self):
235        item = self.GetObj('PACKAGE_NAME = MdePkg')._ParseItem()
236        self.assertEqual(item.Key, 'PACKAGE_NAME')
237        self.assertEqual(item.Value, 'MdePkg')
238
239    def testDecDefine1(self):
240        obj = self.GetObj('PACKAGE_NAME')
241        self.assertRaises(FatalError, obj._ParseItem)
242
243    def testDecDefine2(self):
244        obj = self.GetObj('unknown_key = ')
245        self.assertRaises(FatalError, obj._ParseItem)
246
247    def testDecDefine3(self):
248        obj = self.GetObj('PACKAGE_NAME = ')
249        self.assertRaises(FatalError, obj._ParseItem)
250
251#
252# Test _DecLibraryclass._ParseItem
253#
254class DecLibraryTestCase(unittest.TestCase):
255    def GetObj(self, Content):
256        Obj = _DecLibraryclass(FileContent('dummy', Content.splitlines()))
257        Obj._RawData.CurrentLine = Obj._RawData.GetNextLine()
258        return Obj
259
260    def testNoInc(self):
261        obj = self.GetObj('UefiRuntimeLib')
262        self.assertRaises(FatalError, obj._ParseItem)
263
264    def testEmpty(self):
265        obj = self.GetObj(' | ')
266        self.assertRaises(FatalError, obj._ParseItem)
267
268    def testLibclassNaming(self):
269        obj = self.GetObj('lowercase_efiRuntimeLib|Include/Library/UefiRuntimeLib.h')
270        self.assertRaises(FatalError, obj._ParseItem)
271
272    def testLibclassExt(self):
273        obj = self.GetObj('RuntimeLib|Include/Library/UefiRuntimeLib.no_h')
274        self.assertRaises(FatalError, obj._ParseItem)
275
276    def testLibclassRelative(self):
277        obj = self.GetObj('RuntimeLib|Include/../UefiRuntimeLib.h')
278        self.assertRaises(FatalError, obj._ParseItem)
279
280#
281# Test _DecPcd._ParseItem
282#
283class DecPcdTestCase(unittest.TestCase):
284    def GetObj(self, Content):
285        Obj = _DecPcd(FileContent('dummy', Content.splitlines()))
286        Obj._RawData.CurrentLine = Obj._RawData.GetNextLine()
287        Obj._RawData.CurrentScope = [('PcdsFeatureFlag'.upper(), 'COMMON')]
288        return Obj
289
290    def testOK(self):
291        item = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable|FALSE|BOOLEAN|0x0000000d')._ParseItem()
292        self.assertEqual(item.TokenSpaceGuidCName, 'gEfiMdePkgTokenSpaceGuid')
293        self.assertEqual(item.TokenCName, 'PcdComponentNameDisable')
294        self.assertEqual(item.DefaultValue, 'FALSE')
295        self.assertEqual(item.DatumType, 'BOOLEAN')
296        self.assertEqual(item.TokenValue, '0x0000000d')
297
298    def testNoCvar(self):
299        obj = self.GetObj('123ai.PcdComponentNameDisable|FALSE|BOOLEAN|0x0000000d')
300        self.assertRaises(FatalError, obj._ParseItem)
301
302    def testSplit(self):
303        obj = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable FALSE|BOOLEAN|0x0000000d')
304        self.assertRaises(FatalError, obj._ParseItem)
305
306        obj = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable|FALSE|BOOLEAN|0x0000000d | abc')
307        self.assertRaises(FatalError, obj._ParseItem)
308
309    def testUnknownType(self):
310        obj = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable|FALSE|unknown|0x0000000d')
311        self.assertRaises(FatalError, obj._ParseItem)
312
313    def testVoid(self):
314        obj = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable|abc|VOID*|0x0000000d')
315        self.assertRaises(FatalError, obj._ParseItem)
316
317    def testUINT(self):
318        obj = self.GetObj('gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable|0xabc|UINT8|0x0000000d')
319        self.assertRaises(FatalError, obj._ParseItem)
320
321#
322# Test _DecInclude._ParseItem
323#
324class DecIncludeTestCase(unittest.TestCase):
325    #
326    # Test code to be added
327    #
328    pass
329
330#
331# Test _DecGuid._ParseItem
332#
333class DecGuidTestCase(unittest.TestCase):
334    def GetObj(self, Content):
335        Obj = _DecGuid(FileContent('dummy', Content.splitlines()))
336        Obj._RawData.CurrentLine = Obj._RawData.GetNextLine()
337        Obj._RawData.CurrentScope = [('guids'.upper(), 'COMMON')]
338        return Obj
339
340    def testCValue(self):
341        item = self.GetObj('gEfiIpSecProtocolGuid={ 0xdfb386f7, 0xe100, 0x43ad,'
342                           ' {0x9c, 0x9a, 0xed, 0x90, 0xd0, 0x8a, 0x5e, 0x12 }}')._ParseItem()
343        self.assertEqual(item.GuidCName, 'gEfiIpSecProtocolGuid')
344        self.assertEqual(item.GuidCValue, '{ 0xdfb386f7, 0xe100, 0x43ad, {0x9c, 0x9a, 0xed, 0x90, 0xd0, 0x8a, 0x5e, 0x12 }}')
345
346    def testGuidString(self):
347        item = self.GetObj('gEfiIpSecProtocolGuid=1E73767F-8F52-4603-AEB4-F29B510B6766')._ParseItem()
348        self.assertEqual(item.GuidCName, 'gEfiIpSecProtocolGuid')
349        self.assertEqual(item.GuidCValue, '1E73767F-8F52-4603-AEB4-F29B510B6766')
350
351    def testNoValue1(self):
352        obj = self.GetObj('gEfiIpSecProtocolGuid')
353        self.assertRaises(FatalError, obj._ParseItem)
354
355    def testNoValue2(self):
356        obj = self.GetObj('gEfiIpSecProtocolGuid=')
357        self.assertRaises(FatalError, obj._ParseItem)
358
359    def testNoName(self):
360        obj = self.GetObj('=')
361        self.assertRaises(FatalError, obj._ParseItem)
362
363#
364# Test Dec.__init__
365#
366class DecDecInitTestCase(unittest.TestCase):
367    def testNoDecFile(self):
368        self.assertRaises(FatalError, Dec, 'No_Such_File')
369
370class TmpFile:
371    def __init__(self, File):
372        self.File = File
373
374    def Write(self, Content):
375        try:
376            FileObj = open(self.File, 'w')
377            FileObj.write(Content)
378            FileObj.close()
379        except:
380            pass
381
382    def Remove(self):
383        try:
384            os.remove(self.File)
385        except:
386            pass
387
388#
389# Test Dec._UserExtentionSectionParser
390#
391class DecUESectionTestCase(unittest.TestCase):
392    def setUp(self):
393        self.File = TmpFile('test.dec')
394        self.File.Write(
395'''[userextensions.intel."myid"]
396[userextensions.intel."myid".IA32]
397[userextensions.intel."myid".IA32,]
398[userextensions.intel."myid]
399'''
400        )
401
402    def tearDown(self):
403        self.File.Remove()
404
405    def testUserExtentionHeader(self):
406        dec = Dec('test.dec', False)
407
408        # OK: [userextensions.intel."myid"]
409        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
410        dec._UserExtentionSectionParser()
411        self.assertEqual(len(dec._RawData.CurrentScope), 1)
412        self.assertEqual(dec._RawData.CurrentScope[0][0], 'userextensions'.upper())
413        self.assertEqual(dec._RawData.CurrentScope[0][1], 'intel')
414        self.assertEqual(dec._RawData.CurrentScope[0][2], '"myid"')
415        self.assertEqual(dec._RawData.CurrentScope[0][3], 'COMMON')
416
417        # OK: [userextensions.intel."myid".IA32]
418        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
419        dec._UserExtentionSectionParser()
420        self.assertEqual(len(dec._RawData.CurrentScope), 1)
421        self.assertEqual(dec._RawData.CurrentScope[0][0], 'userextensions'.upper())
422        self.assertEqual(dec._RawData.CurrentScope[0][1], 'intel')
423        self.assertEqual(dec._RawData.CurrentScope[0][2], '"myid"')
424        self.assertEqual(dec._RawData.CurrentScope[0][3], 'IA32')
425
426        # Fail: [userextensions.intel."myid".IA32,]
427        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
428        self.assertRaises(FatalError, dec._UserExtentionSectionParser)
429
430        # Fail: [userextensions.intel."myid]
431        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
432        self.assertRaises(FatalError, dec._UserExtentionSectionParser)
433
434#
435# Test Dec._SectionHeaderParser
436#
437class DecSectionTestCase(unittest.TestCase):
438    def setUp(self):
439        self.File = TmpFile('test.dec')
440        self.File.Write(
441'''[no section start or end
442[,] # empty sub-section
443[unknow_section_name]
444[Includes.IA32.other] # no third one
445[PcdsFeatureFlag, PcdsFixedAtBuild] # feature flag PCD must not be in the same section of other types of PCD
446[Includes.IA32, Includes.IA32]
447[Includes, Includes.IA32] # common cannot be with other arch
448[Includes.IA32, PcdsFeatureFlag] # different section name
449'''     )
450
451    def tearDown(self):
452        self.File.Remove()
453
454    def testSectionHeader(self):
455        dec = Dec('test.dec', False)
456        # [no section start or end
457        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
458        self.assertRaises(FatalError, dec._SectionHeaderParser)
459
460        #[,] # empty sub-section
461        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
462        self.assertRaises(FatalError, dec._SectionHeaderParser)
463
464        # [unknow_section_name]
465        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
466        self.assertRaises(FatalError, dec._SectionHeaderParser)
467
468        # [Includes.IA32.other] # no third one
469        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
470        self.assertRaises(FatalError, dec._SectionHeaderParser)
471
472        # [PcdsFeatureFlag, PcdsFixedAtBuild]
473        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
474        self.assertRaises(FatalError, dec._SectionHeaderParser)
475
476        # [Includes.IA32, Includes.IA32]
477        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
478        dec._SectionHeaderParser()
479        self.assertEqual(len(dec._RawData.CurrentScope), 1)
480        self.assertEqual(dec._RawData.CurrentScope[0][0], 'Includes'.upper())
481        self.assertEqual(dec._RawData.CurrentScope[0][1], 'IA32')
482
483        # [Includes, Includes.IA32] # common cannot be with other arch
484        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
485        self.assertRaises(FatalError, dec._SectionHeaderParser)
486
487        # [Includes.IA32, PcdsFeatureFlag] # different section name not allowed
488        dec._RawData.CurrentLine = CleanString(dec._RawData.GetNextLine())[0]
489        self.assertRaises(FatalError, dec._SectionHeaderParser)
490
491#
492# Test Dec._ParseDecComment
493#
494class DecDecCommentTestCase(unittest.TestCase):
495    def testDecHeadComment(self):
496        File = TmpFile('test.dec')
497        File.Write(
498       '''# abc
499          ##''')
500        dec = Dec('test.dec', False)
501        dec.ParseDecComment()
502        self.assertEqual(len(dec._HeadComment), 2)
503        self.assertEqual(dec._HeadComment[0][0], '# abc')
504        self.assertEqual(dec._HeadComment[0][1], 1)
505        self.assertEqual(dec._HeadComment[1][0], '##')
506        self.assertEqual(dec._HeadComment[1][1], 2)
507        File.Remove()
508
509    def testNoDoubleComment(self):
510        File = TmpFile('test.dec')
511        File.Write(
512       '''# abc
513          #
514          [section_start]''')
515        dec = Dec('test.dec', False)
516        dec.ParseDecComment()
517        self.assertEqual(len(dec._HeadComment), 2)
518        self.assertEqual(dec._HeadComment[0][0], '# abc')
519        self.assertEqual(dec._HeadComment[0][1], 1)
520        self.assertEqual(dec._HeadComment[1][0], '#')
521        self.assertEqual(dec._HeadComment[1][1], 2)
522        File.Remove()
523
524if __name__ == '__main__':
525    import Logger.Logger
526    Logger.Logger.Initialize()
527    unittest.main()
528
529