@@ -1531,6 +1531,221 @@ func TestGit(t *testing.T) {
1531
1531
)
1532
1532
}
1533
1533
1534
+ func TestTreeRootCmd (t * testing.T ) {
1535
+ as := require .New (t )
1536
+
1537
+ tempDir := test .TempExamples (t )
1538
+ configPath := filepath .Join (tempDir , "/treefmt.toml" )
1539
+
1540
+ test .ChangeWorkDir (t , tempDir )
1541
+
1542
+ // basic config
1543
+ cfg := & config.Config {
1544
+ FormatterConfigs : map [string ]* config.Formatter {
1545
+ "echo" : {
1546
+ Command : "echo" , // will not generate any underlying changes in the file
1547
+ Includes : []string {"*" },
1548
+ },
1549
+ },
1550
+ }
1551
+
1552
+ test .WriteConfig (t , configPath , cfg )
1553
+
1554
+ // construct a tree root command with some error logging and dumping output on stdout
1555
+ treeRootCmd := func (output string ) string {
1556
+ return fmt .Sprintf ("bash -c '>&2 echo -e \" some error text\n some more error text\" && echo %s'" , output )
1557
+ }
1558
+
1559
+ // helper for checking the contents of stderr matches our expected debug output
1560
+ checkStderr := func (buf []byte ) {
1561
+ output := string (buf )
1562
+ as .Contains (output , "DEBU tree-root-cmd | stderr: some error text\n " )
1563
+ as .Contains (output , "DEBU tree-root-cmd | stderr: some more error text\n " )
1564
+ }
1565
+
1566
+ // run treefmt with DEBUG logging enabled and with tree root cmd being the root of the temp directory
1567
+ treefmt (t ,
1568
+ withArgs ("-vv" , "--tree-root-cmd" , treeRootCmd (tempDir )),
1569
+ withNoError (t ),
1570
+ withStderr (checkStderr ),
1571
+ withConfig (configPath , cfg ),
1572
+ withStats (t , map [stats.Type ]int {
1573
+ stats .Traversed : 32 ,
1574
+ stats .Matched : 32 ,
1575
+ stats .Formatted : 32 ,
1576
+ stats .Changed : 0 ,
1577
+ }),
1578
+ )
1579
+
1580
+ // run from a subdirectory, mixing things up by specifying the command via an env variable
1581
+ treefmt (t ,
1582
+ withArgs ("-vv" ),
1583
+ withEnv (map [string ]string {
1584
+ "TREEFMT_TREE_ROOT_CMD" : treeRootCmd (filepath .Join (tempDir , "go" )),
1585
+ }),
1586
+ withNoError (t ),
1587
+ withStderr (checkStderr ),
1588
+ withConfig (configPath , cfg ),
1589
+ withStats (t , map [stats.Type ]int {
1590
+ stats .Traversed : 2 ,
1591
+ stats .Matched : 2 ,
1592
+ stats .Formatted : 2 ,
1593
+ stats .Changed : 0 ,
1594
+ }),
1595
+ )
1596
+
1597
+ // run from a subdirectory, mixing things up by specifying the command via config
1598
+ cfg .TreeRootCmd = treeRootCmd (filepath .Join (tempDir , "haskell" ))
1599
+
1600
+ treefmt (t ,
1601
+ withArgs ("-vv" ),
1602
+ withNoError (t ),
1603
+ withStderr (checkStderr ),
1604
+ withConfig (configPath , cfg ),
1605
+ withStats (t , map [stats.Type ]int {
1606
+ stats .Traversed : 7 ,
1607
+ stats .Matched : 7 ,
1608
+ stats .Formatted : 7 ,
1609
+ stats .Changed : 0 ,
1610
+ }),
1611
+ )
1612
+
1613
+ // run with a long-running command (2 seconds or more)
1614
+ treefmt (t ,
1615
+ withArgs (
1616
+ "-vv" ,
1617
+ "--tree-root-cmd" , fmt .Sprintf (
1618
+ "bash -c 'sleep 2 && echo %s'" ,
1619
+ tempDir ,
1620
+ ),
1621
+ ),
1622
+ withError (func (as * require.Assertions , err error ) {
1623
+ as .ErrorContains (err , "tree-root-cmd was killed after taking more than 2s to execute" )
1624
+ }),
1625
+ withConfig (configPath , cfg ),
1626
+ )
1627
+
1628
+ // run with a command that outputs multiple lines
1629
+ treefmt (t ,
1630
+ withArgs (
1631
+ "--tree-root-cmd" , fmt .Sprintf (
1632
+ "bash -c 'echo %s && echo %s'" ,
1633
+ tempDir , tempDir ,
1634
+ ),
1635
+ ),
1636
+ withStderr (func (buf []byte ) {
1637
+ as .Contains (string (buf ), fmt .Sprintf ("ERRO tree-root-cmd | stdout: \n %s\n %s\n " , tempDir , tempDir ))
1638
+ }),
1639
+ withError (func (as * require.Assertions , err error ) {
1640
+ as .ErrorContains (err , "tree-root-cmd cannot output multiple lines" )
1641
+ }),
1642
+ withConfig (configPath , cfg ),
1643
+ )
1644
+ }
1645
+
1646
+ func TestTreeRootExclusivity (t * testing.T ) {
1647
+ tempDir := test .TempExamples (t )
1648
+ configPath := filepath .Join (tempDir , "/treefmt.toml" )
1649
+
1650
+ formatterConfigs := map [string ]* config.Formatter {
1651
+ "echo" : {
1652
+ Command : "echo" , // will not generate any underlying changes in the file
1653
+ Includes : []string {"*" },
1654
+ },
1655
+ }
1656
+
1657
+ test .ChangeWorkDir (t , tempDir )
1658
+
1659
+ assertExclusiveFlag := func (as * require.Assertions , err error ) {
1660
+ as .ErrorContains (err ,
1661
+ "if any flags in the group [tree-root tree-root-cmd tree-root-file] are set none of the others can be;" ,
1662
+ )
1663
+ }
1664
+
1665
+ assertExclusiveConfig := func (as * require.Assertions , err error ) {
1666
+ as .ErrorContains (err ,
1667
+ "at most one of tree-root, tree-root-cmd or tree-root-file can be specified" ,
1668
+ )
1669
+ }
1670
+
1671
+ envValues := map [string ][]string {
1672
+ "tree-root" : {"TREEFMT_TREE_ROOT" , "bar" },
1673
+ "tree-root-cmd" : {"TREEFMT_TREE_ROOT_CMD" , "echo /foo/bar" },
1674
+ "tree-root-file" : {"TREEFMT_TREE_ROOT_FILE" , ".git/config" },
1675
+ }
1676
+
1677
+ flagValues := map [string ][]string {
1678
+ "tree-root" : {"--tree-root" , "bar" },
1679
+ "tree-root-cmd" : {"--tree-root-cmd" , "'echo /foo/bar'" },
1680
+ "tree-root-file" : {"--tree-root-file" , ".git/config" },
1681
+ }
1682
+
1683
+ configValues := map [string ]func (* config.Config ){
1684
+ "tree-root" : func (cfg * config.Config ) {
1685
+ cfg .TreeRoot = "bar"
1686
+ },
1687
+ "tree-root-cmd" : func (cfg * config.Config ) {
1688
+ cfg .TreeRootCmd = "'echo /foo/bar'"
1689
+ },
1690
+ "tree-root-file" : func (cfg * config.Config ) {
1691
+ cfg .TreeRootFile = ".git/config"
1692
+ },
1693
+ }
1694
+
1695
+ invalidCombinations := [][]string {
1696
+ {"tree-root" , "tree-root-cmd" },
1697
+ {"tree-root" , "tree-root-file" },
1698
+ {"tree-root-cmd" , "tree-root-file" },
1699
+ {"tree-root" , "tree-root-cmd" , "tree-root-file" },
1700
+ }
1701
+
1702
+ // TODO we should also test mixing the various methods in the same test e.g. env variable and config value
1703
+ // Given that ultimately everything is being reduced into the config object after parsing from viper, I'm fairly
1704
+ // confident if these tests all pass then the mixed methods should yield the same result.
1705
+
1706
+ // for each set of invalid args, test them with flags, environment variables, and config entries.
1707
+ for _ , combination := range invalidCombinations {
1708
+ // test flags
1709
+ var args []string
1710
+ for _ , key := range combination {
1711
+ args = append (args , flagValues [key ]... )
1712
+ }
1713
+
1714
+ treefmt (t ,
1715
+ withArgs (args ... ),
1716
+ withError (assertExclusiveFlag ),
1717
+ )
1718
+
1719
+ // test env variables
1720
+ env := make (map [string ]string )
1721
+
1722
+ for _ , key := range combination {
1723
+ entry := envValues [key ]
1724
+ env [entry [0 ]] = entry [1 ]
1725
+ }
1726
+
1727
+ treefmt (t ,
1728
+ withEnv (env ),
1729
+ withError (assertExclusiveConfig ),
1730
+ )
1731
+
1732
+ // test config
1733
+ cfg := & config.Config {
1734
+ FormatterConfigs : formatterConfigs ,
1735
+ }
1736
+
1737
+ for _ , key := range combination {
1738
+ entry := configValues [key ]
1739
+ entry (cfg )
1740
+ }
1741
+
1742
+ treefmt (t ,
1743
+ withConfig (configPath , cfg ),
1744
+ withError (assertExclusiveConfig ),
1745
+ )
1746
+ }
1747
+ }
1748
+
1534
1749
func TestPathsArg (t * testing.T ) {
1535
1750
as := require .New (t )
1536
1751
@@ -1998,6 +2213,7 @@ func treefmt(
1998
2213
1999
2214
// set env
2000
2215
for k , v := range opts .env {
2216
+ t .Logf ("setting env %s=%s" , k , v )
2001
2217
t .Setenv (k , v )
2002
2218
}
2003
2219
0 commit comments