...

Source file src/github.com/cpuguy83/go-md2man/v2/md2man/roff_test.go

Documentation: github.com/cpuguy83/go-md2man/v2/md2man

     1  package md2man
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/russross/blackfriday/v2"
     7  )
     8  
     9  type TestParams struct {
    10  	extensions blackfriday.Extensions
    11  }
    12  
    13  func TestCodeBlocks(t *testing.T) {
    14  	tests := []string{
    15  		"```\nsome code\n```\n",
    16  		".nh\n\n.EX\nsome code\n.EE\n",
    17  
    18  		"```bash\necho foo\n```\n",
    19  		".nh\n\n.EX\necho foo\n.EE\n",
    20  
    21  		// make sure literal new lines surrounding the markdown block are preserved as they are intentional
    22  		"```bash\n\nsome code\n\n```",
    23  		".nh\n\n.EX\n\nsome code\n\n.EE\n",
    24  	}
    25  	doTestsParam(t, tests, TestParams{blackfriday.FencedCode})
    26  }
    27  
    28  func TestEmphasis(t *testing.T) {
    29  	tests := []string{
    30  		"nothing inline\n",
    31  		".nh\n\n.PP\nnothing inline\n",
    32  
    33  		"simple *inline* test\n",
    34  		".nh\n\n.PP\nsimple \\fIinline\\fP test\n",
    35  
    36  		"*at the* beginning\n",
    37  		".nh\n\n.PP\n\\fIat the\\fP beginning\n",
    38  
    39  		"at the *end*\n",
    40  		".nh\n\n.PP\nat the \\fIend\\fP\n",
    41  
    42  		"*try two* in *one line*\n",
    43  		".nh\n\n.PP\n\\fItry two\\fP in \\fIone line\\fP\n",
    44  
    45  		"over *two\nlines* test\n",
    46  		".nh\n\n.PP\nover \\fItwo\nlines\\fP test\n",
    47  
    48  		"odd *number of* markers* here\n",
    49  		".nh\n\n.PP\nodd \\fInumber of\\fP markers* here\n",
    50  
    51  		"odd *number\nof* markers* here\n",
    52  		".nh\n\n.PP\nodd \\fInumber\nof\\fP markers* here\n",
    53  
    54  		"simple _inline_ test\n",
    55  		".nh\n\n.PP\nsimple \\fIinline\\fP test\n",
    56  
    57  		"_at the_ beginning\n",
    58  		".nh\n\n.PP\n\\fIat the\\fP beginning\n",
    59  
    60  		"at the _end_\n",
    61  		".nh\n\n.PP\nat the \\fIend\\fP\n",
    62  
    63  		"_try two_ in _one line_\n",
    64  		".nh\n\n.PP\n\\fItry two\\fP in \\fIone line\\fP\n",
    65  
    66  		"over _two\nlines_ test\n",
    67  		".nh\n\n.PP\nover \\fItwo\nlines\\fP test\n",
    68  
    69  		"odd _number of_ markers_ here\n",
    70  		".nh\n\n.PP\nodd \\fInumber of\\fP markers_ here\n",
    71  
    72  		"odd _number\nof_ markers_ here\n",
    73  		".nh\n\n.PP\nodd \\fInumber\nof\\fP markers_ here\n",
    74  
    75  		"mix of *markers_\n",
    76  		".nh\n\n.PP\nmix of *markers_\n",
    77  
    78  		"*What is A\\* algorithm?*\n",
    79  		".nh\n\n.PP\n\\fIWhat is A* algorithm?\\fP\n",
    80  	}
    81  	doTestsInline(t, tests)
    82  }
    83  
    84  func TestStrong(t *testing.T) {
    85  	tests := []string{
    86  		"nothing inline\n",
    87  		".nh\n\n.PP\nnothing inline\n",
    88  
    89  		"simple **inline** test\n",
    90  		".nh\n\n.PP\nsimple \\fBinline\\fP test\n",
    91  
    92  		"**at the** beginning\n",
    93  		".nh\n\n.PP\n\\fBat the\\fP beginning\n",
    94  
    95  		"at the **end**\n",
    96  		".nh\n\n.PP\nat the \\fBend\\fP\n",
    97  
    98  		"**try two** in **one line**\n",
    99  		".nh\n\n.PP\n\\fBtry two\\fP in \\fBone line\\fP\n",
   100  
   101  		"over **two\nlines** test\n",
   102  		".nh\n\n.PP\nover \\fBtwo\nlines\\fP test\n",
   103  
   104  		"odd **number of** markers** here\n",
   105  		".nh\n\n.PP\nodd \\fBnumber of\\fP markers** here\n",
   106  
   107  		"odd **number\nof** markers** here\n",
   108  		".nh\n\n.PP\nodd \\fBnumber\nof\\fP markers** here\n",
   109  
   110  		"simple __inline__ test\n",
   111  		".nh\n\n.PP\nsimple \\fBinline\\fP test\n",
   112  
   113  		"__at the__ beginning\n",
   114  		".nh\n\n.PP\n\\fBat the\\fP beginning\n",
   115  
   116  		"at the __end__\n",
   117  		".nh\n\n.PP\nat the \\fBend\\fP\n",
   118  
   119  		"__try two__ in __one line__\n",
   120  		".nh\n\n.PP\n\\fBtry two\\fP in \\fBone line\\fP\n",
   121  
   122  		"over __two\nlines__ test\n",
   123  		".nh\n\n.PP\nover \\fBtwo\nlines\\fP test\n",
   124  
   125  		"odd __number of__ markers__ here\n",
   126  		".nh\n\n.PP\nodd \\fBnumber of\\fP markers__ here\n",
   127  
   128  		"odd __number\nof__ markers__ here\n",
   129  		".nh\n\n.PP\nodd \\fBnumber\nof\\fP markers__ here\n",
   130  
   131  		"mix of **markers__\n",
   132  		".nh\n\n.PP\nmix of **markers__\n",
   133  
   134  		"**`/usr`** : this folder is named `usr`\n",
   135  		".nh\n\n.PP\n\\fB\\fB/usr\\fR\\fP : this folder is named \\fBusr\\fR\n",
   136  
   137  		"**`/usr`** :\n\n this folder is named `usr`\n",
   138  		".nh\n\n.PP\n\\fB\\fB/usr\\fR\\fP :\n\n.PP\nthis folder is named \\fBusr\\fR\n",
   139  	}
   140  	doTestsInline(t, tests)
   141  }
   142  
   143  func TestEmphasisMix(t *testing.T) {
   144  	tests := []string{
   145  		"***triple emphasis***\n",
   146  		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
   147  
   148  		"***triple\nemphasis***\n",
   149  		".nh\n\n.PP\n\\fB\\fItriple\nemphasis\\fP\\fP\n",
   150  
   151  		"___triple emphasis___\n",
   152  		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
   153  
   154  		"***triple emphasis___\n",
   155  		".nh\n\n.PP\n***triple emphasis___\n",
   156  
   157  		"*__triple emphasis__*\n",
   158  		".nh\n\n.PP\n\\fI\\fBtriple emphasis\\fP\\fP\n",
   159  
   160  		"__*triple emphasis*__\n",
   161  		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
   162  
   163  		"**improper *nesting** is* bad\n",
   164  		".nh\n\n.PP\n\\fBimproper *nesting\\fP is* bad\n",
   165  
   166  		"*improper **nesting* is** bad\n",
   167  		".nh\n\n.PP\n*improper \\fBnesting* is\\fP bad\n",
   168  	}
   169  	doTestsInline(t, tests)
   170  }
   171  
   172  func TestCodeSpan(t *testing.T) {
   173  	tests := []string{
   174  		"`source code`\n",
   175  		".nh\n\n.PP\n\\fBsource code\\fR\n",
   176  
   177  		"` source code with spaces `\n",
   178  		".nh\n\n.PP\n\\fBsource code with spaces\\fR\n",
   179  
   180  		"` source code with spaces `not here\n",
   181  		".nh\n\n.PP\n\\fBsource code with spaces\\fRnot here\n",
   182  
   183  		"a `single marker\n",
   184  		".nh\n\n.PP\na `single marker\n",
   185  
   186  		"a single multi-tick marker with ``` no text\n",
   187  		".nh\n\n.PP\na single multi-tick marker with ``` no text\n",
   188  
   189  		"markers with ` ` a space\n",
   190  		".nh\n\n.PP\nmarkers with  a space\n",
   191  
   192  		"`source code` and a `stray\n",
   193  		".nh\n\n.PP\n\\fBsource code\\fR and a `stray\n",
   194  
   195  		"`source *with* _awkward characters_ in it`\n",
   196  		".nh\n\n.PP\n\\fBsource *with* _awkward characters_ in it\\fR\n",
   197  
   198  		"`split over\ntwo lines`\n",
   199  		".nh\n\n.PP\n\\fBsplit over\ntwo lines\\fR\n",
   200  
   201  		"```multiple ticks``` for the marker\n",
   202  		".nh\n\n.PP\n\\fBmultiple ticks\\fR for the marker\n",
   203  
   204  		"```multiple ticks `with` ticks inside```\n",
   205  		".nh\n\n.PP\n\\fBmultiple ticks `with` ticks inside\\fR\n",
   206  	}
   207  	doTestsInline(t, tests)
   208  }
   209  
   210  func TestListLists(t *testing.T) {
   211  	tests := []string{
   212  		"\n\n**[grpc]**\n: Section for gRPC socket listener settings. Contains three properties:\n - **address** (Default: \"/run/containerd/containerd.sock\")\n - **uid** (Default: 0)\n - **gid** (Default: 0)",
   213  		".nh\n\n.TP\n\\fB[grpc]\\fP\nSection for gRPC socket listener settings. Contains three properties:\n.RS\n.IP \\(bu 2\n\\fBaddress\\fP (Default: \"/run/containerd/containerd.sock\")\n.IP \\(bu 2\n\\fBuid\\fP (Default: 0)\n.IP \\(bu 2\n\\fBgid\\fP (Default: 0)\n\n.RE\n\n",
   214  		"Definition title\n: Definition description one\n: And two\n: And three\n",
   215  		".nh\n\n.TP\nDefinition title\nDefinition description one\n\nAnd two\n\nAnd three\n",
   216  	}
   217  	doTestsParam(t, tests, TestParams{blackfriday.DefinitionLists})
   218  }
   219  
   220  func TestLineBreak(t *testing.T) {
   221  	tests := []string{
   222  		"this line  \nhas a break\n",
   223  		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
   224  
   225  		"this line \ndoes not\n",
   226  		".nh\n\n.PP\nthis line\ndoes not\n",
   227  
   228  		"this line\\\ndoes not\n",
   229  		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
   230  
   231  		"this line\\ \ndoes not\n",
   232  		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
   233  
   234  		"this has an   \nextra space\n",
   235  		".nh\n\n.PP\nthis has an\n.br\nextra space\n",
   236  	}
   237  	doTestsInline(t, tests)
   238  
   239  	tests = []string{
   240  		"this line  \nhas a break\n",
   241  		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
   242  
   243  		"this line \ndoes not\n",
   244  		".nh\n\n.PP\nthis line\ndoes not\n",
   245  
   246  		"this line\\\nhas a break\n",
   247  		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
   248  
   249  		"this line\\ \ndoes not\n",
   250  		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
   251  
   252  		"this has an   \nextra space\n",
   253  		".nh\n\n.PP\nthis has an\n.br\nextra space\n",
   254  	}
   255  	doTestsInlineParam(t, tests, TestParams{
   256  		extensions: blackfriday.BackslashLineBreak,
   257  	})
   258  }
   259  
   260  func TestTable(t *testing.T) {
   261  	tests := []string{
   262  		`
   263  | Animal               | Color         |
   264  | --------------| --- |
   265  | elephant        | Gray. The elephant is very gray.  |
   266  | wombat     | No idea.      |
   267  | zebra        | Sometimes black and sometimes white, depending on the stripe.     |
   268  | robin | red. |
   269  `,
   270  		`'\" t
   271  .nh
   272  
   273  .TS
   274  allbox;
   275  l l 
   276  l l .
   277  \fBAnimal\fP	\fBColor\fP
   278  elephant	T{
   279  Gray. The elephant is very gray.
   280  T}
   281  wombat	No idea.
   282  zebra	T{
   283  Sometimes black and sometimes white, depending on the stripe.
   284  T}
   285  robin	red.
   286  .TE
   287  `,
   288  	}
   289  	doTestsInlineParam(t, tests, TestParams{blackfriday.Tables})
   290  }
   291  
   292  func TestTableWithEmptyCell(t *testing.T) {
   293  	tests := []string{
   294  		`
   295  | Col1     | Col2  | Col3 |
   296  |:---------|:-----:|:----:| 
   297  | row one  |       |      | 
   298  | row two  | x     |      |
   299  `,
   300  		`'\" t
   301  .nh
   302  
   303  .TS
   304  allbox;
   305  l l l 
   306  l l l .
   307  \fBCol1\fP	\fBCol2\fP	\fBCol3\fP
   308  row one		
   309  row two	x	
   310  .TE
   311  `,
   312  	}
   313  	doTestsInlineParam(t, tests, TestParams{blackfriday.Tables})
   314  }
   315  
   316  func TestTableWrapping(t *testing.T) {
   317  	tests := []string{
   318  		`
   319  | Col1        | Col2                                             |
   320  | ----------- | ------------------------------------------------ |
   321  | row one     | This is a short line.                            |
   322  | row\|two    | Col1 should not wrap.                            |
   323  | row three   | no\|wrap                                         |
   324  | row four    | Inline _cursive_ should not wrap.                |
   325  | row five    | Inline ` + "`code markup`" + ` should not wrap.  |
   326  | row six     | A line that's longer than 30 characters with inline ` + "`code markup`" + ` or _cursive_ should not wrap.  |
   327  | row seven   | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eu ipsum eget tortor aliquam accumsan. Quisque ac turpis convallis, sagittis urna ac, tempor est. Mauris nibh arcu, hendrerit id eros sed, sodales lacinia ex. Suspendisse sed condimentum urna, vitae mattis lectus. Mauris imperdiet magna vel purus pretium, id interdum libero. |
   328  `,
   329  		`'\" t
   330  .nh
   331  
   332  .TS
   333  allbox;
   334  l l 
   335  l l .
   336  \fBCol1\fP	\fBCol2\fP
   337  row one	This is a short line.
   338  row|two	Col1 should not wrap.
   339  row three	no|wrap
   340  row four	Inline \fIcursive\fP should not wrap.
   341  row five	Inline \fBcode markup\fR should not wrap.
   342  row six	T{
   343  A line that's longer than 30 characters with inline \fBcode markup\fR or \fIcursive\fP should not wrap.
   344  T}
   345  row seven	T{
   346  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eu ipsum eget tortor aliquam accumsan. Quisque ac turpis convallis, sagittis urna ac, tempor est. Mauris nibh arcu, hendrerit id eros sed, sodales lacinia ex. Suspendisse sed condimentum urna, vitae mattis lectus. Mauris imperdiet magna vel purus pretium, id interdum libero.
   347  T}
   348  .TE
   349  `,
   350  	}
   351  	doTestsInlineParam(t, tests, TestParams{blackfriday.Tables})
   352  }
   353  
   354  func TestLinks(t *testing.T) {
   355  	tests := []string{
   356  		"See [docs](https://docs.docker.com/) for\nmore",
   357  		".nh\n\n.PP\nSee docs\n\\[la]https://docs.docker.com/\\[ra] for\nmore\n",
   358  		"See [docs](https://docs-foo.docker.com/) for\nmore",
   359  		".nh\n\n.PP\nSee docs\n\\[la]https://docs\\-foo.docker.com/\\[ra] for\nmore\n",
   360  		"See <https://docs-foo.docker.com/> for\nmore",
   361  		".nh\n\n.PP\nSee \n\\[la]https://docs\\-foo.docker.com/\\[ra] for\nmore\n",
   362  	}
   363  	doTestsInline(t, tests)
   364  }
   365  
   366  func TestEscapeCharacters(t *testing.T) {
   367  	tests := []string{
   368  		"Test-one_two&three\\four~five",
   369  		".nh\n\n.PP\nTest-one_two&three\\\\four~five\n",
   370  		"'foo'\n'bar'",
   371  		".nh\n\n.PP\n\\&'foo'\n\\&'bar'\n",
   372  	}
   373  	doTestsInline(t, tests)
   374  }
   375  
   376  func TestSpan(t *testing.T) {
   377  	tests := []string{
   378  		"Text containing a <span>html span</span> element\n",
   379  		".nh\n\n.PP\nText containing a html span element\n",
   380  
   381  		`Text containing an inline <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"><image href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" height="200" width="200"/></svg>SVG image`,
   382  		".nh\n\n.PP\nText containing an inline SVG image\n",
   383  
   384  		"Text containing a <span id=\"e-123\" class=\"foo\">html span</span> element\n",
   385  		".nh\n\n.PP\nText containing a html span element\n",
   386  	}
   387  	doTestsInline(t, tests)
   388  }
   389  
   390  func TestEmails(t *testing.T) {
   391  	tests := []string{
   392  		`April 2014, Originally compiled by William Henry (whenry at redhat dot com)
   393  based on docker.com source material and internal work.
   394  June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
   395  July 2014, updated by Sven Dowideit (SvenDowideit@home.org.au)
   396  `,
   397  		`.nh
   398  
   399  .PP
   400  April 2014, Originally compiled by William Henry (whenry at redhat dot com)
   401  based on docker.com source material and internal work.
   402  June 2014, updated by Sven Dowideit SvenDowideit@home.org.au
   403  \[la]mailto:SvenDowideit@home.org.au\[ra]
   404  July 2014, updated by Sven Dowideit (SvenDowideit@home.org.au)
   405  `,
   406  	}
   407  	doTestsInline(t, tests)
   408  }
   409  
   410  func TestComments(t *testing.T) {
   411  	blockTests := []string{
   412  		"First paragraph\n\n<!-- Comment, HTML should be separated by blank lines -->\n\nSecond paragraph\n",
   413  		".nh\n\n.PP\nFirst paragraph\n\n.PP\nSecond paragraph\n",
   414  	}
   415  	doTestsParam(t, blockTests, TestParams{})
   416  
   417  	inlineTests := []string{
   418  		"Text with a com<!--...-->ment in the middle\n",
   419  		".nh\n\n.PP\nText with a comment in the middle\n",
   420  	}
   421  	doTestsInlineParam(t, inlineTests, TestParams{})
   422  }
   423  
   424  func execRecoverableTestSuite(t *testing.T, tests []string, params TestParams, suite func(candidate *string)) {
   425  	// Catch and report panics. This is useful when running 'go test -v' on
   426  	// the integration server. When developing, though, crash dump is often
   427  	// preferable, so recovery can be easily turned off with doRecover = false.
   428  	var candidate string
   429  	const doRecover = true
   430  	if doRecover {
   431  		defer func() {
   432  			if err := recover(); err != nil {
   433  				t.Errorf("\npanic while processing [%#v]: %s\n", candidate, err)
   434  			}
   435  		}()
   436  	}
   437  	suite(&candidate)
   438  }
   439  
   440  func runMarkdown(input string, params TestParams) string {
   441  	renderer := NewRoffRenderer()
   442  	return string(blackfriday.Run([]byte(input), blackfriday.WithRenderer(renderer),
   443  		blackfriday.WithExtensions(params.extensions)))
   444  }
   445  
   446  func doTestsParam(t *testing.T, tests []string, params TestParams) {
   447  	execRecoverableTestSuite(t, tests, params, func(candidate *string) {
   448  		for i := 0; i+1 < len(tests); i += 2 {
   449  			input := tests[i]
   450  			t.Run(input, func(t *testing.T) {
   451  				*candidate = input
   452  				expected := tests[i+1]
   453  				actual := runMarkdown(*candidate, params)
   454  				if actual != expected {
   455  					t.Errorf("\nInput   [%#v]\nExpected[%#v]\nActual  [%#v]",
   456  						*candidate, expected, actual)
   457  				}
   458  
   459  				// now test every substring to stress test bounds checking
   460  				if !testing.Short() {
   461  					for start := 0; start < len(input); start++ {
   462  						for end := start + 1; end <= len(input); end++ {
   463  							*candidate = input[start:end]
   464  							runMarkdown(*candidate, params)
   465  						}
   466  					}
   467  				}
   468  			})
   469  		}
   470  	})
   471  }
   472  
   473  func doTestsInline(t *testing.T, tests []string) {
   474  	doTestsInlineParam(t, tests, TestParams{})
   475  }
   476  
   477  func doTestsInlineParam(t *testing.T, tests []string, params TestParams) {
   478  	params.extensions |= blackfriday.Strikethrough
   479  	doTestsParam(t, tests, params)
   480  }
   481  

View as plain text