packages feed

NGLess-1.4.0: Tests-Src/Tests.hs

{- Copyright 2013-2021 NGLess Authors
 - License: MIT
 -}
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
-- | Unit tests are their own programme.
--
-- Unit tests written in Haskell have less overhead than full integration tests
-- in the tests/ directory, but are not always as convenient.

module Main where

import Test.Tasty
import Test.Tasty.TH
import Test.Tasty.HUnit
import Text.Parsec (parse)
import Text.Parsec.Combinator (eof)

import System.Directory (removeDirectoryRecursive)
import qualified Data.Vector.Storable as VS
import qualified Data.ByteString.Char8 as B

import qualified Data.Conduit as C
import           Data.Conduit ((.|))
import           Control.Monad.State.Strict (execState, modify')
import           Data.Convertible (convert)
import           Data.Conduit.Algorithms.Async (conduitPossiblyCompressedFile)

import Language
import Interpret
import Tokens
import FileManagement
import NGLess
import NGLess.NGLEnvironment (setupTestEnvironment)

import Interpretation.Unique

import Data.Sam
import Data.FastQ
import Utils.Conduit
import Utils.Samtools (samBamConduit)
import Utils.Here
import qualified Data.GFF as GFF

import Tests.Utils
import Tests.Count (tgroup_Count)
import Tests.FastQ (tgroup_FastQ)
import Tests.IntGroups (tgroup_IntGroups)
import Tests.Language (tgroup_Language)
import Tests.LoadFQDirectory (tgroup_LoadFQDirectory)
import Tests.NGLessAPI (tgroup_NGLessAPI)
import Tests.Parse (tgroup_Parse)
import Tests.Select (tgroup_Select)
import Tests.Types (tgroup_Types)
import Tests.Validation (tgroup_Validation)
import Tests.Vector (tgroup_Vector)
import Tests.Write (tgroup_Write)

test_FastQ      = [tgroup_FastQ]
test_Validation = [tgroup_Validation]
test_Count      = [tgroup_Count]
test_Parse      = [tgroup_Parse]
test_Types      = [tgroup_Types]
test_NGLessAPI  = [tgroup_NGLessAPI]
test_Vector     = [tgroup_Vector]
test_IntGroups  = [tgroup_IntGroups]
test_Select     = [tgroup_Select]
test_Language   = [tgroup_Language]
test_LoadFqDir  = [tgroup_LoadFQDirectory]
test_Write      = [tgroup_Write]

-- The main test driver sets up the config options then uses the automatically
-- generated function
main = do
    setupTestEnvironment
    $(defaultMainGenerator)
    removeDirectoryRecursive "testing_directory_tmp"

-- Test Tokens module
tokenize' fn t = map snd <$> (tokenize fn t)

case_tok_cr = TNewLine @=? (case parse (Tokens.eol <* eof) "test" "\r\n" of { Right t -> t; Left _ -> error "Parse failed"; })
case_tok_single_line_comment = tokenize' "test" with_comment @?= Right expected
    where
        with_comment = "a=0# comment\nb=1\n"
        expected = [TWord "a",TOperator '=',TExpr (ConstInt 0),TNewLine,TWord "b",TOperator '=',TExpr (ConstInt 1),TNewLine]

case_tok_single_line_comment_cstyle = tokenize' "test" with_comment @?= Right expected
    where
        with_comment = "a=0// comment\nb=1\n"
        expected = [TWord "a",TOperator '=',TExpr (ConstInt 0),TNewLine,TWord "b",TOperator '=',TExpr (ConstInt 1),TNewLine]

case_tok_multi_line_comment = tokenize' "test" with_comment @?= Right expected
    where
        with_comment = "a=0/* This\n\nwith\nlines*/\nb=1\n"
        expected = [TWord "a",TOperator '=',TExpr (ConstInt 0),TIndent 0,TNewLine,TWord "b",TOperator '=',TExpr (ConstInt 1),TNewLine]

case_tok_word_ = tokenize' "test" "word_with_underscore" @?= Right expected
    where
        expected = [TWord "word_with_underscore"]



-- Test Types
case_indent_comment = isOk "ParseFailed" $ parsetest indent_comment
case_indent_space = isOk "ParseFailed" $ parsetest indent_space

indent_comment = "ngless '0.0'\n\
    \reads = fastq('input1.fq')\n\
    \reads = preprocess(reads) using |read|:\n\
    \    read = read[5:]\n\
    \    # comment \n"

indent_space  = "ngless '0.0'\n\
    \reads = fastq('input1.fq')\n\
    \reads = preprocess(reads) using |read|:\n\
    \    read = read[5:]\n\
    \    \n"


-- Type Validate pre process operations
sr i s q = NGOShortRead (ShortRead i s $ VS.generate (B.length q) (convert . B.index q))

case_pre_process_indexation_1 = _evalIndex' (sr "@IRIS" "AGTACCAA" "aa`aaaaa") [Just (NGOInteger 5), Nothing] @?= (sr "@IRIS" "CAA" "aaa")
case_pre_process_indexation_2 = _evalIndex' (sr "@IRIS" "AGTACCAA" "aa`aaaaa") [Nothing, Just (NGOInteger 3)] @?= (sr "@IRIS" "AGT" "aa`")
case_pre_process_indexation_3 = _evalIndex' (sr "@IRIS" "AGTACCAA" "aa`aaaaa") [Just (NGOInteger 2), Just (NGOInteger 5)] @?= (sr "@IRIS" "TAC" "`aa")

_evalIndex' a b = case _evalIndex a b of
    Right v -> v
    Left err -> error (show err)

case_pre_process_length_1 = _evalUnary UOpLen (sr "@IRIS" "AGTACCAA" "aa`aaaaa") @?= Right (NGOInteger 8)

case_bop_gte_1 = evalBinary BOpGTE (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_gte_2 = evalBinary BOpGTE (NGOInteger 11) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_gte_3 = evalBinary BOpGTE (NGOInteger 10) (NGOInteger 11) @?= Right (NGOBool False)

case_bop_gt_1 = evalBinary BOpGT (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool False)
case_bop_gt_2 = evalBinary BOpGT (NGOInteger 11) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_gt_3 = evalBinary BOpGT (NGOInteger 10) (NGOInteger 11) @?= Right (NGOBool False)

case_bop_lt_1 = evalBinary BOpLT (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool False)
case_bop_lt_2 = evalBinary BOpLT (NGOInteger 11) (NGOInteger 10) @?= Right (NGOBool False)
case_bop_lt_3 = evalBinary BOpLT (NGOInteger 10) (NGOInteger 11) @?= Right (NGOBool True)

case_bop_lte_1 = evalBinary BOpLTE (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_lte_2 = evalBinary BOpLTE (NGOInteger 11) (NGOInteger 10) @?= Right (NGOBool False)
case_bop_lte_3 = evalBinary BOpLTE (NGOInteger 10) (NGOInteger 11) @?= Right (NGOBool True)

case_bop_eq_1 = evalBinary BOpEQ (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_eq_2 = evalBinary BOpEQ (NGOInteger 10) (NGOInteger 0) @?= Right (NGOBool False)

case_bop_neq_1 = evalBinary BOpNEQ (NGOInteger 0) (NGOInteger 10) @?= Right (NGOBool True)
case_bop_neq_2 = evalBinary BOpNEQ (NGOInteger 10) (NGOInteger 10) @?= Right (NGOBool False)

case_bop_add_1 = evalBinary BOpAdd (NGOInteger 0) (NGOInteger 10) @?= Right (NGOInteger 10)
case_bop_add_2 = evalBinary BOpAdd (NGOInteger 10) (NGOInteger 0) @?= Right (NGOInteger 10)
case_bop_add_3 = evalBinary BOpAdd (NGOInteger 10) (NGOInteger 10) @?= Right (NGOInteger 20)

case_bop_mul_1 = evalBinary BOpMul (NGOInteger 0) (NGOInteger 10) @?= Right (NGOInteger 0)
case_bop_mul_2 = evalBinary BOpMul (NGOInteger 10) (NGOInteger 0) @?= Right (NGOInteger 0)
case_bop_mul_3 = evalBinary BOpMul (NGOInteger 10) (NGOInteger 10) @?= Right (NGOInteger 100)

case_bop_add_path_1 = evalBinary BOpPathAppend (NGOString "dir") (NGOString "file") @?= Right (NGOString "dir/file")
case_bop_add_path_2 = evalBinary BOpPathAppend (NGOString "dir/subdir") (NGOString "file") @?= Right (NGOString "dir/subdir/file")
case_bop_add_path_3 = evalBinary BOpPathAppend (NGOString "dir/subdir/") (NGOString "file") @?= Right (NGOString "dir/subdir/file")
case_bop_add_path_4 = evalBinary BOpPathAppend (NGOString "../dir/subdir/") (NGOString "file") @?= Right (NGOString "../dir/subdir/file")
case_bop_add_path_5 = evalBinary BOpPathAppend (NGOString "/abs/dir/subdir/") (NGOString "file") @?= Right (NGOString "/abs/dir/subdir/file")

case_uop_minus_1 = _evalUnary UOpMinus (NGOInteger 10) @?= Right (NGOInteger (-10))
case_uop_minus_2 = _evalUnary UOpMinus (NGOInteger (-10)) @?= Right (NGOInteger 10)

--

case_template_id = takeBaseNameNoExtensions "a/B/c/d/xpto_1.fq" @?= takeBaseNameNoExtensions "a/B/c/d/xpto_1.fq"
case_template    = takeBaseNameNoExtensions "a/B/c/d/xpto_1.fq" @?= "xpto_1"

samStats :: FilePath -> NGLessIO (Int, Int, Int)
samStats fname = C.runConduit (samBamConduit fname .| linesVC 1024 .| samStatsC) >>= runNGLess

case_sam20 = do
        sam <- testNGLessIO $ asTempFile sam20 "sam"  >>= samStats
        sam @?= (5,0,0)
    where
        sam20 = [here|
@SQ	SN:I	LN:230218
@PG	ID:bwa	PN:bwa	VN:0.7.7-r441	CL:/home/luispedro/.local/share/ngless/bin/ngless-0.0.0-bwa mem -t 1 /home/luispedro/.local/share/ngless/data/sacCer3/Sequence/BWAIndex/reference.fa.gz /tmp/preprocessed_sample20.fq1804289383846930886.gz
IRIS:7:1:17:394#0	4	*	0	0	*	*	0	0	GTCAGGACAAGAAAGACAANTCCAATTNACATT	aaabaa`]baaaaa_aab]D^^`b`aYDW]aba	AS:i:0	XS:i:0
IRIS:7:1:17:800#0	4	*	0	0	*	*	0	0	GGAAACACTACTTAGGCTTATAAGATCNGGTTGCGG	ababbaaabaaaaa`]`ba`]`aaaaYD\\_a``XT	AS:i:0	XS:i:0
IRIS:7:1:17:1757#0	4	*	0	0	*	*	0	0	TTTTCTCGACGATTTCCACTCCTGGTCNAC	aaaaaa``aaa`aaaa_^a```]][Z[DY^	AS:i:0	XS:i:0
IRIS:7:1:17:1479#0	4	*	0	0	*	*	0	0	CATATTGTAGGGTGGATCTCGAAAGATATGAAAGAT	abaaaaa`a```^aaaaa`_]aaa`aaa__a_X]``	AS:i:0	XS:i:0
IRIS:7:1:17:150#0	4	*	0	0	*	*	0	0	TGATGTACTATGCATATGAACTTGTATGCAAAGTGG	abaabaa`aaaaaaa^ba_]]aaa^aaaaa_^][aa	AS:i:0	XS:i:0
|]
-- Parse GFF lines



case_trim_attrs_1  = GFF._trimString " x = 10" @?= "x = 10"
case_trim_attrs_2  = GFF._trimString " x = 10 " @?= "x = 10"
case_trim_attrs_3  = GFF._trimString "x = 10 " @?= "x = 10"
case_trim_attrs_4  = GFF._trimString "x = 10" @?= "x = 10"
case_trim_attrs_5  = GFF._trimString "   X    " @?= "X"


case_parse_gff_line = GFF.readGffLine gff_line @?= Right gff_structure
    where
        gff_line = "chrI\tunknown\texon\t4124\t4358\t.\t-\t.\tgene_id \"Y74C9A.3\"; transcript_id \"NM_058260\"; gene_name \"Y74C9A.3\"; p_id \"P23728\"; tss_id \"TSS14501\";"
        gff_structure  = GFF.GffLine "chrI" "unknown" "exon" 4124 4358 Nothing GFF.GffNegStrand (-1) attrsExpected
        attrsExpected = [("gene_id","Y74C9A.3"), ("transcript_id" ,"NM_058260"), ("gene_name", "Y74C9A.3"), ("p_id", "P23728"), ("tss_id", "TSS14501")]

-- _parseGffAttributes
case_parse_gff_atributes_normal_1 = GFF._parseGffAttributes "ID=chrI;dbxref=NCBI:NC_001133;Name=chrI" @?= [("ID","chrI"),("dbxref","NCBI:NC_001133"),("Name","chrI")]
case_parse_gff_atributes_normal_2 = GFF._parseGffAttributes "gene_id=chrI;dbxref=NCBI:NC_001133;Name=chrI" @?= [("gene_id","chrI"),("dbxref","NCBI:NC_001133"),("Name","chrI")]
case_parse_gff_atributes_trail_del = GFF._parseGffAttributes "gene_id=chrI;dbxref=NCBI:NC_001133;Name=chrI;" @?= [("gene_id","chrI"),("dbxref","NCBI:NC_001133"),("Name","chrI")]
case_parse_gff_atributes_trail_del_space = GFF._parseGffAttributes "gene_id=chrI;dbxref=NCBI:NC_001133;Name=chrI; " @?= [("gene_id","chrI"),("dbxref","NCBI:NC_001133"),("Name","chrI")]


case_calc_sam_stats = testNGLessIO (samStats "test_samples/sample.sam.gz") >>= \r ->
  r @?=  (2772,1310,1299)

--- Unique.hs

--File "test_samples/data_set_repeated.fq" has 216 reads in which 54 are unique.

countC = loop (0 :: Int)
    where
        loop !n = C.await >>= maybe (return n) (const (loop $ n+1))
make_unique_test n = let enc = SolexaEncoding in do
    nuniq <- testNGLessIO $ do
        newfp <- performUnique "test_samples/data_set_repeated.fq" enc n
        C.runConduit $
            conduitPossiblyCompressedFile newfp
                .| linesC
                .| fqDecodeC "testing" enc
                .| countC
    let n' = min n 4
    nuniq @?=  (n' * 54)

case_unique_1 = make_unique_test 1
case_unique_2 = make_unique_test 2
case_unique_3 = make_unique_test 3
case_unique_4 = make_unique_test 4
case_unique_5 = make_unique_test 5


case_recursiveAnalyze = execState (recursiveAnalyse countFcalls expr) 0 @?= (1 :: Int)
    where
        countFcalls (FunctionCall  _ _ _ _) = modify' (+1)
        countFcalls _ = return ()

        expr = Assignment
                    (Variable "varname")
                    (FunctionCall (FuncName "count")
                        (Lookup Nothing (Variable "mapped"))
                        [(Variable "features", ListExpression [ConstStr "seqname"])
                            ,(Variable "multiple", ConstSymbol "all1")]
                        Nothing)


case_expand_path = do
    expandPath' "/nothing1/file.txt" [] @?=  ["/nothing1/file.txt"]
    expandPath' "/nothing2/file.txt" [undefined] @?=  ["/nothing2/file.txt"]
    expandPath' "/nothing3/file.txt" ["/home/luispedro/my-directory"] @?=  ["/nothing3/file.txt"]
    expandPath' "<>/nothing4/file.txt" ["/home/luispedro/my-directory1"] @?=  ["/home/luispedro/my-directory1/nothing4/file.txt"]
    expandPath' "<>/nothing4/file.txt" ["refs=/home/luispedro/my-directory1"] @?=  []
    expandPath' "<>/nothing/file.txt" ["/home/luispedro/my-directory"
                                      ,"/home/alternative/your-directory"] @?=  ["/home/luispedro/my-directory/nothing/file.txt"
                                                                                ,"/home/alternative/your-directory/nothing/file.txt"]
    expandPath' "<refs>/nothing/file.txt" ["/home/luispedro/my-directory"
                                      ,"/home/alternative/your-directory"] @?=  ["/home/luispedro/my-directory/nothing/file.txt"
                                                                                ,"/home/alternative/your-directory/nothing/file.txt"]
    expandPath' "<refs>/nothing/file.txt" ["refs=/home/luispedro/my-directory"
                                      ,"/home/alternative/your-directory"] @?=  ["/home/luispedro/my-directory/nothing/file.txt"
                                                                                ,"/home/alternative/your-directory/nothing/file.txt"]
    expandPath' "<refs>/nothing/file.txt" ["refs=/home/luispedro/my-directory"
                                      ,"nope=/home/alternative/your-directory"] @?=  ["/home/luispedro/my-directory/nothing/file.txt"]
    expandPath' "<refs>/nothing/file.txt" ["other=/home/luispedro/my-directory"
                                      ,"nope=/home/alternative/your-directory"] @?=  []
    expandPath' "<refs>/nothing/file.txt" [] @?= []