tmqUdf.py 18.4 KB
Newer Older
P
plum-lihui 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from distutils.log import error
import taos
import sys
import time
import socket
import os
import threading
import subprocess
import platform

from util.log import *
from util.sql import *
from util.cases import *
from util.dnodes import *
from util.common import *
sys.path.append("./7-tmq")
from tmqCommon import *

class TDTestCase:
P
plum-lihui 已提交
20 21 22 23 24
    def __init__(self):
        self.snapshot   = 0
        self.vgroups    = 4
        self.ctbNum     = 1
        self.rowsPerTbl = 1000
G
Ganlin Zhao 已提交
25

26
    def init(self, conn, logSql, replicaVar=1):
27
        self.replicaVar = int(replicaVar)
P
plum-lihui 已提交
28 29 30
        tdLog.debug(f"start to excute {__file__}")
        tdSql.init(conn.cursor())
        #tdSql.init(conn.cursor(), logSql)  # output sql.txt file
G
Ganlin Zhao 已提交
31

P
plum-lihui 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    def prepare_udf_so(self):
        selfPath = os.path.dirname(os.path.realpath(__file__))

        if ("community" in selfPath):
            projPath = selfPath[:selfPath.find("community")]
        else:
            projPath = selfPath[:selfPath.find("tests")]
        print(projPath)

        if platform.system().lower() == 'windows':
            self.libudf1 = subprocess.Popen('(for /r %s %%i in ("udf1.d*") do @echo %%i)|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8")
            if (not tdDnodes.dnodes[0].remoteIP == ""):
                tdDnodes.dnodes[0].remote_conn.get(tdDnodes.dnodes[0].config["path"]+'/debug/build/lib/libudf1.so',projPath+"\\debug\\build\\lib\\")
                self.libudf1 = self.libudf1.replace('udf1.dll','libudf1.so')
        else:
            self.libudf1 = subprocess.Popen('find %s -name "libudf1.so"|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8")
        self.libudf1 = self.libudf1.replace('\r','').replace('\n','')
        return

    def create_udf_function(self):
        # create  scalar functions
        tdSql.execute("create function udf1 as '%s' outputtype int bufSize 8;"%self.libudf1)

        functions = tdSql.getResult("show functions")
        function_nums = len(functions)
        if function_nums == 1:
            tdLog.info("create one udf functions success ")
        else:
            tdLog.exit("create udf functions fail")
        return

    def checkFileContent(self, consumerId, queryString):
        buildPath = tdCom.getBuildPath()
        cfgPath = tdCom.getClientCfgPath()
        dstFile = '%s/../log/dstrows_%d.txt'%(cfgPath, consumerId)
        cmdStr = '%s/build/bin/taos -c %s -s "%s >> %s"'%(buildPath, cfgPath, queryString, dstFile)
        tdLog.info(cmdStr)
        os.system(cmdStr)
G
Ganlin Zhao 已提交
70

P
plum-lihui 已提交
71 72 73 74 75
        consumeRowsFile = '%s/../log/consumerid_%d.txt'%(cfgPath, consumerId)
        tdLog.info("rows file: %s, %s"%(consumeRowsFile, dstFile))

        consumeFile = open(consumeRowsFile, mode='r')
        queryFile = open(dstFile, mode='r')
G
Ganlin Zhao 已提交
76

P
plum-lihui 已提交
77 78 79 80 81 82
        # skip first line for it is schema
        queryFile.readline()

        while True:
            dst = queryFile.readline()
            src = consumeFile.readline()
G
Ganlin Zhao 已提交
83

P
plum-lihui 已提交
84 85 86 87 88
            if dst:
                if dst != src:
                    tdLog.exit("consumerId %d consume rows is not match the rows by direct query"%consumerId)
            else:
                break
P
plum-lihui 已提交
89
        return
P
plum-lihui 已提交
90

P
plum-lihui 已提交
91 92 93
    def prepareTestEnv(self):
        tdLog.printNoPrefix("======== prepare test env include database, stable, ctables, and insert data: ")
        paraDict = {'dbName':     'dbt',
P
plum-lihui 已提交
94 95 96 97 98 99
                    'dropFlag':   1,
                    'event':      '',
                    'vgroups':    4,
                    'stbName':    'stb',
                    'colPrefix':  'c',
                    'tagPrefix':  't',
P
plum-lihui 已提交
100 101
                    'colSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1},{'type': 'TIMESTAMP', 'count':1}],
                    'tagSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1}],
P
plum-lihui 已提交
102
                    'ctbPrefix':  'ctb',
P
plum-lihui 已提交
103
                    'ctbStartIdx': 0,
P
plum-lihui 已提交
104 105
                    'ctbNum':     1,
                    'rowsPerTbl': 1000,
P
plum-lihui 已提交
106
                    'batchNum':   100,
P
plum-lihui 已提交
107
                    'startTs':    1640966400000,  # 2022-01-01 00:00:00.000
P
plum-lihui 已提交
108
                    'pollDelay':  10,
P
plum-lihui 已提交
109
                    'showMsg':    1,
P
plum-lihui 已提交
110 111
                    'showRow':    1,
                    'snapshot':   0}
P
plum-lihui 已提交
112

P
plum-lihui 已提交
113 114 115
        paraDict['vgroups'] = self.vgroups
        paraDict['ctbNum'] = self.ctbNum
        paraDict['rowsPerTbl'] = self.rowsPerTbl
G
Ganlin Zhao 已提交
116

P
plum-lihui 已提交
117
        tmqCom.initConsumerTable()
P
plum-lihui 已提交
118
        tdCom.create_database(tdSql, paraDict["dbName"],paraDict["dropFlag"], vgroups=paraDict["vgroups"],replica=1)
P
plum-lihui 已提交
119
        tdLog.info("create stb")
P
plum-lihui 已提交
120
        tmqCom.create_stable(tdSql, dbName=paraDict["dbName"],stbName=paraDict["stbName"])
P
plum-lihui 已提交
121
        tdLog.info("create ctb")
P
plum-lihui 已提交
122 123
        tmqCom.create_ctable(tdSql, dbName=paraDict["dbName"],stbName=paraDict["stbName"],ctbPrefix=paraDict['ctbPrefix'],
                             ctbNum=paraDict["ctbNum"],ctbStartIdx=paraDict['ctbStartIdx'])
P
plum-lihui 已提交
124
        tdLog.info("insert data")
P
plum-lihui 已提交
125 126 127 128 129 130
        tmqCom.insert_data_interlaceByMultiTbl(tsql=tdSql,dbName=paraDict["dbName"],ctbPrefix=paraDict["ctbPrefix"],
                                               ctbNum=paraDict["ctbNum"],rowsPerTbl=paraDict["rowsPerTbl"],batchNum=paraDict["batchNum"],
                                               startTs=paraDict["startTs"],ctbStartIdx=paraDict['ctbStartIdx'])
        # tmqCom.insert_data_with_autoCreateTbl(tsql=tdSql,dbName=paraDict["dbName"],stbName=paraDict["stbName"],ctbPrefix="ctbx",
        #                                       ctbNum=paraDict["ctbNum"],rowsPerTbl=paraDict["rowsPerTbl"],batchNum=paraDict["batchNum"],
        #                                       startTs=paraDict["startTs"],ctbStartIdx=paraDict['ctbStartIdx'])
G
Ganlin Zhao 已提交
131 132

        # tdLog.info("restart taosd to ensure that the data falls into the disk")
P
plum-lihui 已提交
133 134
        # tdSql.query("flush database %s"%(paraDict['dbName']))
        return
G
Ganlin Zhao 已提交
135

P
plum-lihui 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
    def tmqCase1(self):
        tdLog.printNoPrefix("======== test case 1: one sub table")
        paraDict = {'dbName':     'dbt',
                    'dropFlag':   1,
                    'event':      '',
                    'vgroups':    4,
                    'stbName':    'stb',
                    'colPrefix':  'c',
                    'tagPrefix':  't',
                    'colSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1},{'type': 'TIMESTAMP', 'count':1}],
                    'tagSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1}],
                    'ctbPrefix':  'ctb',
                    'ctbStartIdx': 0,
                    'ctbNum':     1,
                    'rowsPerTbl': 1000,
                    'batchNum':   100,
                    'startTs':    1640966400000,  # 2022-01-01 00:00:00.000
P
plum-lihui 已提交
153
                    'pollDelay':  10,
P
plum-lihui 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
                    'showMsg':    1,
                    'showRow':    1,
                    'snapshot':   0}
        paraDict['snapshot'] = self.snapshot
        paraDict['vgroups'] = self.vgroups
        paraDict['ctbNum'] = self.ctbNum
        paraDict['rowsPerTbl'] = self.rowsPerTbl

        topicNameList = ['topic1', 'topic2']
        expectRowsList = []
        tmqCom.initConsumerTable()
        # tdCom.create_database(tdSql, paraDict["dbName"],paraDict["dropFlag"], vgroups=4,replica=1)
        # tdLog.info("create stb")
        # tdCom.create_stable(tdSql, dbname=paraDict["dbName"],stbname=paraDict["stbName"], column_elm_list=paraDict['colSchema'], tag_elm_list=paraDict['tagSchema'])
        # tdLog.info("create ctb")
        # tdCom.create_ctable(tdSql, dbname=paraDict["dbName"],stbname=paraDict["stbName"],tag_elm_list=paraDict['tagSchema'],count=paraDict["ctbNum"], default_ctbname_prefix=paraDict['ctbPrefix'])
        # tdLog.info("insert data")
        # tmqCom.insert_data_1(tdSql,paraDict["dbName"],paraDict["ctbPrefix"],paraDict["ctbNum"],paraDict["rowsPerTbl"],paraDict["batchNum"],paraDict["startTs"])
G
Ganlin Zhao 已提交
172

P
plum-lihui 已提交
173
        tdLog.info("create topics from stb with filter")
P
plum-lihui 已提交
174
        queryString = "select ts,c1,udf1(c1),c2,udf1(c2) from %s.%s where c1 %% 7 == 0" %(paraDict['dbName'], paraDict['stbName'])
P
plum-lihui 已提交
175 176 177
        sqlString = "create topic %s as %s" %(topicNameList[0], queryString)
        tdLog.info("create topic sql: %s"%sqlString)
        tdSql.execute(sqlString)
G
Ganlin Zhao 已提交
178
        tdSql.query(queryString)
P
plum-lihui 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191
        expectRowsList.append(tdSql.getRows())

        # init consume info, and start tmq_sim, then check consume result
        tdLog.info("insert consume info to consume processor")
        consumerId   = 0
        expectrowcnt = paraDict["rowsPerTbl"] * paraDict["ctbNum"]
        topicList    = topicNameList[0]
        ifcheckdata  = 1
        ifManualCommit = 1
        keyList      = 'group.id:cgrp1, enable.auto.commit:false, auto.commit.interval.ms:6000, auto.offset.reset:earliest'
        tmqCom.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit)

        tdLog.info("start consume processor")
P
plum-lihui 已提交
192
        tmqCom.startTmqSimProcess(paraDict['pollDelay'],paraDict["dbName"],paraDict['showMsg'], paraDict['showRow'],snapshot=paraDict['snapshot'])
P
plum-lihui 已提交
193

G
Ganlin Zhao 已提交
194
        tdLog.info("wait the consume result")
P
plum-lihui 已提交
195 196
        expectRows = 1
        resultList = tmqCom.selectConsumeResult(expectRows)
G
Ganlin Zhao 已提交
197

P
plum-lihui 已提交
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
        if expectRowsList[0] != resultList[0]:
            tdLog.info("expect consume rows: %d, act consume rows: %d"%(expectRowsList[0], resultList[0]))
            tdLog.exit("0 tmq consume rows error!")

        self.checkFileContent(consumerId, queryString)
        tdLog.printNoPrefix("consumerId %d check data ok!"%(consumerId))


        # reinit consume info, and start tmq_sim, then check consume result
        tmqCom.initConsumerTable()

        queryString = "select ts, c1,udf1(c1),sin(udf1(c2)), log(udf1(c2)) from %s.%s where udf1(c1) == 88 or sin(udf1(c1)) > 0" %(paraDict['dbName'], paraDict['stbName'])
        sqlString = "create topic %s as %s" %(topicNameList[1], queryString)
        tdLog.info("create topic sql: %s"%sqlString)
        tdSql.execute(sqlString)
G
Ganlin Zhao 已提交
213
        tdSql.query(queryString)
P
plum-lihui 已提交
214 215 216 217 218 219 220
        expectRowsList.append(tdSql.getRows())

        consumerId   = 1
        topicList    = topicNameList[1]
        tmqCom.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit)

        tdLog.info("start consume processor")
P
plum-lihui 已提交
221
        tmqCom.startTmqSimProcess(paraDict['pollDelay'],paraDict["dbName"],paraDict['showMsg'], paraDict['showRow'],snapshot=paraDict['snapshot'])
P
plum-lihui 已提交
222

G
Ganlin Zhao 已提交
223
        tdLog.info("wait the consume result")
P
plum-lihui 已提交
224 225 226 227 228 229 230 231 232
        expectRows = 1
        resultList = tmqCom.selectConsumeResult(expectRows)
        if expectRowsList[1] != resultList[0]:
            tdLog.info("expect consume rows: %d, act consume rows: %d"%(expectRowsList[1], resultList[0]))
            tdLog.exit("1 tmq consume rows error!")

        self.checkFileContent(consumerId, queryString)
        tdLog.printNoPrefix("consumerId %d check data ok!"%(consumerId))

G
Ganlin Zhao 已提交
233
        time.sleep(10)
P
plum-lihui 已提交
234 235 236 237
        for i in range(len(topicNameList)):
            tdSql.query("drop topic %s"%topicNameList[i])

        tdLog.printNoPrefix("======== test case 1 end ...... ")
G
Ganlin Zhao 已提交
238

P
plum-lihui 已提交
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    def tmqCase2(self):
        tdLog.printNoPrefix("======== test case 2: one sub table, consume with auto create tble and insert data")
        paraDict = {'dbName':     'dbt',
                    'dropFlag':   1,
                    'event':      '',
                    'vgroups':    4,
                    'stbName':    'stb',
                    'colPrefix':  'c',
                    'tagPrefix':  't',
                    'colSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1},{'type': 'TIMESTAMP', 'count':1}],
                    'tagSchema':   [{'type': 'INT', 'count':1},{'type': 'BIGINT', 'count':1},{'type': 'DOUBLE', 'count':1},{'type': 'BINARY', 'len':32, 'count':1},{'type': 'NCHAR', 'len':32, 'count':1}],
                    'ctbPrefix':  'ctb',
                    'ctbStartIdx': 0,
                    'ctbNum':     1,
                    'rowsPerTbl': 1000,
                    'batchNum':   100,
                    'startTs':    1640966400000,  # 2022-01-01 00:00:00.000
P
plum-lihui 已提交
256
                    'pollDelay':  10,
P
plum-lihui 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
                    'showMsg':    1,
                    'showRow':    1,
                    'snapshot':   0}
        paraDict['snapshot'] = self.snapshot
        paraDict['vgroups'] = self.vgroups
        paraDict['ctbNum'] = self.ctbNum
        paraDict['rowsPerTbl'] = self.rowsPerTbl

        topicNameList = ['topic1', 'topic2']
        expectRowsList = []
        tmqCom.initConsumerTable()
        # tdCom.create_database(tdSql, paraDict["dbName"],paraDict["dropFlag"], vgroups=4,replica=1)
        # tdLog.info("create stb")
        # tdCom.create_stable(tdSql, dbname=paraDict["dbName"],stbname=paraDict["stbName"], column_elm_list=paraDict['colSchema'], tag_elm_list=paraDict['tagSchema'])
        # tdLog.info("create ctb")
        # tdCom.create_ctable(tdSql, dbname=paraDict["dbName"],stbname=paraDict["stbName"],tag_elm_list=paraDict['tagSchema'],count=paraDict["ctbNum"], default_ctbname_prefix=paraDict['ctbPrefix'])
        # tdLog.info("insert data")
        # tmqCom.insert_data_1(tdSql,paraDict["dbName"],paraDict["ctbPrefix"],paraDict["ctbNum"],paraDict["rowsPerTbl"],paraDict["batchNum"],paraDict["startTs"])
G
Ganlin Zhao 已提交
275

P
plum-lihui 已提交
276 277 278 279 280
        tdLog.info("create topics from stb with filter")
        queryString = "select ts,c1,udf1(c1),c2,udf1(c2) from %s.%s where c1 %% 7 == 0" %(paraDict['dbName'], paraDict['stbName'])
        sqlString = "create topic %s as %s" %(topicNameList[0], queryString)
        tdLog.info("create topic sql: %s"%sqlString)
        tdSql.execute(sqlString)
G
Ganlin Zhao 已提交
281
        # tdSql.query(queryString)
P
plum-lihui 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
        # expectRowsList.append(tdSql.getRows())

        # init consume info, and start tmq_sim, then check consume result
        tdLog.info("insert consume info to consume processor")
        consumerId   = 2
        expectrowcnt = paraDict["rowsPerTbl"] * paraDict["ctbNum"] * 2
        topicList    = topicNameList[0]
        ifcheckdata  = 1
        ifManualCommit = 1
        keyList      = 'group.id:cgrp1, enable.auto.commit:false, auto.commit.interval.ms:6000, auto.offset.reset:earliest'
        tmqCom.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit)

        tdLog.info("start consume processor")
        tmqCom.startTmqSimProcess(paraDict['pollDelay'],paraDict["dbName"],paraDict['showMsg'], paraDict['showRow'],snapshot=paraDict['snapshot'])

G
Ganlin Zhao 已提交
297
        paraDict['startTs'] = paraDict['startTs'] + int(self.rowsPerTbl)
P
plum-lihui 已提交
298 299 300 301
        tmqCom.insert_data_interlaceByMultiTbl(tsql=tdSql,dbName=paraDict["dbName"],ctbPrefix=paraDict["ctbPrefix"],
                                               ctbNum=paraDict["ctbNum"],rowsPerTbl=paraDict["rowsPerTbl"],batchNum=paraDict["batchNum"],
                                               startTs=paraDict["startTs"],ctbStartIdx=paraDict['ctbStartIdx'])

G
Ganlin Zhao 已提交
302
        tdLog.info("wait the consume result")
P
plum-lihui 已提交
303 304
        expectRows = 1
        resultList = tmqCom.selectConsumeResult(expectRows)
G
Ganlin Zhao 已提交
305 306

        tdSql.query(queryString)
P
plum-lihui 已提交
307
        expectRowsList.append(tdSql.getRows())
G
Ganlin Zhao 已提交
308

P
plum-lihui 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322
        if expectRowsList[0] != resultList[0]:
            tdLog.info("expect consume rows: %d, act consume rows: %d"%(expectRowsList[0], resultList[0]))
            tdLog.exit("2 tmq consume rows error!")

        self.checkFileContent(consumerId, queryString)
        tdLog.printNoPrefix("consumerId %d check data ok!"%(consumerId))

        # reinit consume info, and start tmq_sim, then check consume result
        tmqCom.initConsumerTable()

        queryString = "select ts, c1,udf1(c1),sin(udf1(c2)), log(udf1(c2)) from %s.%s where udf1(c1) == 88 or sin(udf1(c1)) > 0" %(paraDict['dbName'], paraDict['stbName'])
        sqlString = "create topic %s as %s" %(topicNameList[1], queryString)
        tdLog.info("create topic sql: %s"%sqlString)
        tdSql.execute(sqlString)
G
Ganlin Zhao 已提交
323
        tdSql.query(queryString)
P
plum-lihui 已提交
324 325 326 327 328 329 330 331 332
        expectRowsList.append(tdSql.getRows())

        consumerId   = 3
        topicList    = topicNameList[1]
        tmqCom.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit)

        tdLog.info("start consume processor")
        tmqCom.startTmqSimProcess(paraDict['pollDelay'],paraDict["dbName"],paraDict['showMsg'], paraDict['showRow'],snapshot=paraDict['snapshot'])

G
Ganlin Zhao 已提交
333
        tdLog.info("wait the consume result")
P
plum-lihui 已提交
334 335 336 337 338 339 340 341 342
        expectRows = 1
        resultList = tmqCom.selectConsumeResult(expectRows)
        if expectRowsList[1] != resultList[0]:
            tdLog.info("expect consume rows: %d, act consume rows: %d"%(expectRowsList[1], resultList[0]))
            tdLog.exit("3 tmq consume rows error!")

        self.checkFileContent(consumerId, queryString)
        tdLog.printNoPrefix("consumerId %d check data ok!"%(consumerId))

G
Ganlin Zhao 已提交
343
        time.sleep(10)
P
plum-lihui 已提交
344 345 346 347
        for i in range(len(topicNameList)):
            tdSql.query("drop topic %s"%topicNameList[i])

        tdLog.printNoPrefix("======== test case 2 end ...... ")
P
plum-lihui 已提交
348 349

    def run(self):
P
plum-lihui 已提交
350
        # tdSql.prepare()
P
plum-lihui 已提交
351 352
        self.prepare_udf_so()
        self.create_udf_function()
G
Ganlin Zhao 已提交
353

P
plum-lihui 已提交
354 355 356 357 358
        tdLog.printNoPrefix("=============================================")
        tdLog.printNoPrefix("======== snapshot is 0: only consume from wal")
        self.prepareTestEnv()
        self.tmqCase1()
        self.tmqCase2()
G
Ganlin Zhao 已提交
359

P
plum-lihui 已提交
360 361 362 363
        tdLog.printNoPrefix("====================================================================")
        tdLog.printNoPrefix("======== snapshot is 1: firstly consume from tsbs, and then from wal")
        self.prepareTestEnv()
        self.snapshot = 1
P
plum-lihui 已提交
364
        self.tmqCase1()
P
plum-lihui 已提交
365
        self.tmqCase2()
P
plum-lihui 已提交
366 367 368 369 370 371 372 373 374

    def stop(self):
        tdSql.close()
        tdLog.success(f"{__file__} successfully executed")

event = threading.Event()

tdCases.addLinux(__file__, TDTestCase())
tdCases.addWindows(__file__, TDTestCase())