VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTest.cpp@ 89051

Last change on this file since 89051 was 89051, checked in by vboxsync, 4 years ago

Audio/ValKit: More code for VKAT audio test set generation; initial implementation now can pack up tests sets as .tar.gz files. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.9 KB
Line 
1/* $Id: AudioTest.cpp 89051 2021-05-14 18:58:11Z vboxsync $ */
2/** @file
3 * Audio testing routines.
4 * Common code which is being used by the ValidationKit and the debug / ValdikationKit audio driver(s).
5 */
6
7/*
8 * Copyright (C) 2021 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23
24#include <package-generated.h>
25#include "product-generated.h"
26
27#include <iprt/buildconfig.h>
28#include <iprt/dir.h>
29#include <iprt/file.h>
30#include <iprt/inifile.h>
31#include <iprt/list.h>
32#include <iprt/rand.h>
33#include <iprt/system.h>
34#include <iprt/uuid.h>
35#include <iprt/vfs.h>
36#include <iprt/zip.h>
37
38#define _USE_MATH_DEFINES
39#include <math.h> /* sin, M_PI */
40
41#include <VBox/version.h>
42#include <VBox/vmm/pdmaudioifs.h>
43#include <VBox/vmm/pdmaudioinline.h>
44
45#include "AudioTest.h"
46
47
48/*********************************************************************************************************************************
49* Defines *
50*********************************************************************************************************************************/
51/** The test manifest file name. */
52#define AUDIOTEST_MANIFEST_FILE_STR "vkat_manifest.ini"
53/** The current test manifest version. */
54#define AUDIOTEST_MANIFEST_VER 1
55
56/** Test manifest header name. */
57#define AUDIOTEST_INI_SEC_HDR_STR "header"
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68/** Well-known frequency selection test tones. */
69static const double s_aAudioTestToneFreqsHz[] =
70{
71 349.2282 /*F4*/,
72 440.0000 /*A4*/,
73 523.2511 /*C5*/,
74 698.4565 /*F5*/,
75 880.0000 /*A5*/,
76 1046.502 /*C6*/,
77 1174.659 /*D6*/,
78 1396.913 /*F6*/,
79 1760.0000 /*A6*/
80};
81
82/**
83 * Initializes a test tone by picking a random but well-known frequency (in Hz).
84 *
85 * @returns Randomly picked frequency (in Hz).
86 * @param pTone Pointer to test tone to initialize.
87 * @param pProps PCM properties to use for the test tone.
88 */
89double AudioTestToneInitRandom(PAUDIOTESTTONE pTone, PPDMAUDIOPCMPROPS pProps)
90{
91 /* Pick a frequency from our selection, so that every time a recording starts
92 * we'll hopfully generate a different note. */
93 pTone->rdFreqHz = s_aAudioTestToneFreqsHz[RTRandU32Ex(0, RT_ELEMENTS(s_aAudioTestToneFreqsHz) - 1)];
94 pTone->rdFixed = 2.0 * M_PI * pTone->rdFreqHz / PDMAudioPropsHz(pProps);
95 pTone->uSample = 0;
96
97 memcpy(&pTone->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
98
99 pTone->enmType = AUDIOTESTTONETYPE_SINE; /* Only type implemented so far. */
100
101 return pTone->rdFreqHz;
102}
103
104/**
105 * Writes (and iterates) a given test tone to an output buffer.
106 *
107 * @returns VBox status code.
108 * @param pTone Pointer to test tone to write.
109 * @param pvBuf Pointer to output buffer to write test tone to.
110 * @param cbBuf Size (in bytes) of output buffer.
111 * @param pcbWritten How many bytes were written on success.
112 */
113int AudioTestToneGenerate(PAUDIOTESTTONE pTone, void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
114{
115 /*
116 * Clear the buffer first so we don't need to think about additional channels.
117 */
118 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pTone->Props, cbBuf);
119
120 /* Input cbBuf not necessarily is aligned to the frames, so re-calculate it. */
121 const uint32_t cbToWrite = PDMAudioPropsFramesToBytes(&pTone->Props, cFrames);
122
123 PDMAudioPropsClearBuffer(&pTone->Props, pvBuf, cbBuf, cFrames);
124
125 /*
126 * Generate the select sin wave in the first channel:
127 */
128 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pTone->Props);
129 double const rdFixed = pTone->rdFixed;
130 uint64_t iSrcFrame = pTone->uSample;
131 switch (PDMAudioPropsSampleSize(&pTone->Props))
132 {
133 case 1:
134 /* untested */
135 if (PDMAudioPropsIsSigned(&pTone->Props))
136 {
137 int8_t *piSample = (int8_t *)pvBuf;
138 while (cFrames-- > 0)
139 {
140 *piSample = (int8_t)(126 /*Amplitude*/ * sin(rdFixed * iSrcFrame));
141 iSrcFrame++;
142 piSample += cbFrame;
143 }
144 }
145 else
146 {
147 /* untested */
148 uint8_t *pbSample = (uint8_t *)pvBuf;
149 while (cFrames-- > 0)
150 {
151 *pbSample = (uint8_t)(126 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x80);
152 iSrcFrame++;
153 pbSample += cbFrame;
154 }
155 }
156 break;
157
158 case 2:
159 if (PDMAudioPropsIsSigned(&pTone->Props))
160 {
161 int16_t *piSample = (int16_t *)pvBuf;
162 while (cFrames-- > 0)
163 {
164 *piSample = (int16_t)(32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame));
165 iSrcFrame++;
166 piSample = (int16_t *)((uint8_t *)piSample + cbFrame);
167 }
168 }
169 else
170 {
171 /* untested */
172 uint16_t *puSample = (uint16_t *)pvBuf;
173 while (cFrames-- > 0)
174 {
175 *puSample = (uint16_t)(32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x8000);
176 iSrcFrame++;
177 puSample = (uint16_t *)((uint8_t *)puSample + cbFrame);
178 }
179 }
180 break;
181
182 case 4:
183 /* untested */
184 if (PDMAudioPropsIsSigned(&pTone->Props))
185 {
186 int32_t *piSample = (int32_t *)pvBuf;
187 while (cFrames-- > 0)
188 {
189 *piSample = (int32_t)((32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame));
190 iSrcFrame++;
191 piSample = (int32_t *)((uint8_t *)piSample + cbFrame);
192 }
193 }
194 else
195 {
196 uint32_t *puSample = (uint32_t *)pvBuf;
197 while (cFrames-- > 0)
198 {
199 *puSample = (uint32_t)((32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame) + UINT32_C(0x80000000));
200 iSrcFrame++;
201 puSample = (uint32_t *)((uint8_t *)puSample + cbFrame);
202 }
203 }
204 break;
205
206 default:
207 AssertFailedReturn(VERR_NOT_SUPPORTED);
208 }
209
210 pTone->uSample = iSrcFrame;
211
212 if (pcbWritten)
213 *pcbWritten = cbToWrite;
214
215 return VINF_SUCCESS;
216}
217
218/**
219 * Initializes an audio test tone parameters struct with random values.
220 * @param pToneParams Test tone parameters to initialize.
221 * @param pProps PCM properties to use for the test tone.
222 */
223int AudioTestToneParamsInitRandom(PAUDIOTESTTONEPARMS pToneParams, PPDMAUDIOPCMPROPS pProps)
224{
225 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
226
227 memcpy(&pToneParams->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
228
229 /** @todo Make this a bit more sophisticated later, e.g. muting and prequel/sequel are not very balanced. */
230
231 pToneParams->msPrequel = RTRandU32Ex(0, RT_MS_5SEC);
232#ifdef DEBUG_andy
233 pToneParams->msDuration = RTRandU32Ex(0, RT_MS_1SEC);
234#else
235 pToneParams->msDuration = RTRandU32Ex(0, RT_MS_10SEC); /** @todo Probably a bit too long, but let's see. */
236#endif
237 pToneParams->msSequel = RTRandU32Ex(0, RT_MS_5SEC);
238 pToneParams->uVolumePercent = RTRandU32Ex(0, 100);
239
240 return VINF_SUCCESS;
241}
242
243/**
244 * Creates a new path (directory) for a specific audio test set tag.
245 *
246 * @returns VBox status code.
247 * @param pszPath On input, specifies the absolute base path where to create the test set path.
248 * On output this specifies the absolute path created.
249 * @param cbPath Size (in bytes) of \a pszPath.
250 * @param pszTag Tag to use for path creation.
251 *
252 * @note Can be used multiple times with the same tag; a sub directory with an ISO time string will be used
253 * on each call.
254 */
255int AudioTestPathCreate(char *pszPath, size_t cbPath, const char *pszTag)
256{
257 AssertReturn(strlen(pszTag) <= AUDIOTEST_TAG_MAX, VERR_INVALID_PARAMETER);
258
259 int rc;
260
261 char szTag[RTUUID_STR_LENGTH + 1];
262 if (pszTag)
263 {
264 rc = RTStrCopy(szTag, sizeof(szTag), pszTag);
265 AssertRCReturn(rc, rc);
266 }
267 else /* Create an UUID if no tag is specified. */
268 {
269 RTUUID UUID;
270 rc = RTUuidCreate(&UUID);
271 AssertRCReturn(rc, rc);
272 rc = RTUuidToStr(&UUID, szTag, sizeof(szTag));
273 AssertRCReturn(rc, rc);
274 }
275
276 char szName[RT_ELEMENTS(AUDIOTEST_PATH_PREFIX_STR) + AUDIOTEST_TAG_MAX + 4];
277 if (RTStrPrintf2(szName, sizeof(szName), "%s-%s", AUDIOTEST_PATH_PREFIX_STR, szTag) < 0)
278 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
279
280 rc = RTPathAppend(pszPath, cbPath, szName);
281 AssertRCReturn(rc, rc);
282
283 char szTime[64];
284 RTTIMESPEC time;
285 if (!RTTimeSpecToString(RTTimeNow(&time), szTime, sizeof(szTime)))
286 return VERR_BUFFER_UNDERFLOW;
287
288 rc = RTPathAppend(pszPath, cbPath, szTime);
289 AssertRCReturn(rc, rc);
290
291 return RTDirCreateFullPath(pszPath, RTFS_UNIX_IRWXU);
292}
293
294/**
295 * Writes string data to a test set manifest.
296 *
297 * @returns VBox status code.
298 * @param pSet Test set to write manifest for.
299 * @param pszFormat Format string to write.
300 * @param args Variable arguments for \a pszFormat.
301 */
302static int audioTestManifestWriteV(PAUDIOTESTSET pSet, const char *pszFormat, va_list args)
303{
304 char *psz = NULL;
305 RTStrAPrintfV(&psz, pszFormat, args);
306 AssertPtr(psz);
307
308 /** @todo Use RTIniFileWrite once its implemented. */
309 int rc = RTFileWrite(pSet->f.hFile, psz, strlen(psz), NULL);
310 AssertRC(rc);
311
312 RTStrFree(psz);
313
314 return rc;
315}
316
317/**
318 * Writes a terminated string line to a test set manifest.
319 * Convenience function.
320 *
321 * @returns VBox status code.
322 * @param pSet Test set to write manifest for.
323 * @param pszFormat Format string to write.
324 * @param ... Variable arguments for \a pszFormat. Optional.
325 */
326static int audioTestManifestWriteLn(PAUDIOTESTSET pSet, const char *pszFormat, ...)
327{
328 va_list va;
329 va_start(va, pszFormat);
330
331 int rc = audioTestManifestWriteV(pSet, pszFormat, va);
332 AssertRC(rc);
333
334 va_end(va);
335
336 /** @todo Keep it as simple as possible for now. Improve this later. */
337 rc = RTFileWrite(pSet->f.hFile, "\n", strlen("\n"), NULL);
338 AssertRC(rc);
339
340 return rc;
341}
342
343/**
344 * Writes a section entry to a test set manifest.
345 *
346 * @returns VBox status code.
347 * @param pSet Test set to write manifest for.
348 * @param pszSection Format string of section to write.
349 * @param ... Variable arguments for \a pszSection. Optional.
350 */
351static int audioTestManifestWriteSection(PAUDIOTESTSET pSet, const char *pszSection, ...)
352{
353 va_list va;
354 va_start(va, pszSection);
355
356 /** @todo Keep it as simple as possible for now. Improve this later. */
357 int rc = RTFileWrite(pSet->f.hFile, "[", strlen("["), NULL);
358 AssertRC(rc);
359
360 rc = audioTestManifestWriteV(pSet, pszSection, va);
361 AssertRC(rc);
362
363 rc = RTFileWrite(pSet->f.hFile, "]\n", strlen("]\n"), NULL);
364 AssertRC(rc);
365
366 va_end(va);
367
368 return rc;
369}
370
371/**
372 * Initializes an audio test set, internal function.
373 * @param pSet Test set to initialize.
374 */
375static void audioTestSetInitInternal(PAUDIOTESTSET pSet)
376{
377 pSet->f.hFile = NIL_RTFILE;
378
379 RTListInit(&pSet->lstObj);
380}
381
382/**
383 * Returns whether a test set's manifest file is open (and thus ready) or not.
384 *
385 * @returns \c true if open (and ready), or \c false if not.
386 * @retval VERR_
387 * @param pSet Test set to return open status for.
388 */
389static bool audioTestManifestIsOpen(PAUDIOTESTSET pSet)
390{
391 if ( pSet->enmMode == AUDIOTESTSETMODE_TEST
392 && pSet->f.hFile != NIL_RTFILE)
393 return true;
394 else if ( pSet->enmMode == AUDIOTESTSETMODE_VERIFY
395 && pSet->f.hIniFile != NIL_RTINIFILE)
396 return true;
397
398 return false;
399}
400
401/**
402 * Initializes an audio test error description.
403 *
404 * @param pErr Test error description to initialize.
405 */
406static void audioTestErrorDescInit(PAUDIOTESTERRORDESC pErr)
407{
408 RTListInit(&pErr->List);
409 pErr->cErrors = 0;
410}
411
412/**
413 * Destroys an audio test error description.
414 *
415 * @param pErr Test error description to destroy.
416 */
417void AudioTestErrorDescDestroy(PAUDIOTESTERRORDESC pErr)
418{
419 if (!pErr)
420 return;
421
422 PAUDIOTESTERRORENTRY pErrEntry, pErrEntryNext;
423 RTListForEachSafe(&pErr->List, pErrEntry, pErrEntryNext, AUDIOTESTERRORENTRY, Node)
424 {
425 RTListNodeRemove(&pErrEntry->Node);
426
427 RTMemFree(pErrEntry);
428
429 Assert(pErr->cErrors);
430 pErr->cErrors--;
431 }
432
433 Assert(pErr->cErrors == 0);
434}
435
436/**
437 * Returns if an audio test error description contains any errors or not.
438 *
439 * @returns \c true if it contains errors, or \c false if not.
440 *
441 * @param pErr Test error description to return error status for.
442 */
443bool AudioTestErrorDescFailed(PAUDIOTESTERRORDESC pErr)
444{
445 if (pErr->cErrors)
446 {
447 Assert(!RTListIsEmpty(&pErr->List));
448 return true;
449 }
450
451 return false;
452}
453
454/**
455 * Adds a single error entry to an audio test error description, va_list version.
456 *
457 * @returns VBox status code.
458 * @param pErr Test error description to add entry for.
459 * @param rc Result code of entry to add.
460 * @param pszDesc Error description format string to add.
461 * @param args Optional format arguments of \a pszDesc to add.
462 */
463static int audioTestErrorDescAddV(PAUDIOTESTERRORDESC pErr, int rc, const char *pszDesc, va_list args)
464{
465 PAUDIOTESTERRORENTRY pEntry = (PAUDIOTESTERRORENTRY)RTMemAlloc(sizeof(AUDIOTESTERRORENTRY));
466 AssertReturn(pEntry, VERR_NO_MEMORY);
467
468 if (RTStrPrintf2V(pEntry->szDesc, sizeof(pEntry->szDesc), pszDesc, args) < 0)
469 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
470
471 pEntry->rc = rc;
472
473 RTListAppend(&pErr->List, &pEntry->Node);
474
475 pErr->cErrors++;
476
477 return VINF_SUCCESS;
478}
479
480/**
481 * Adds a single error entry to an audio test error description, va_list version.
482 *
483 * @returns VBox status code.
484 * @param pErr Test error description to add entry for.
485 * @param pszDesc Error description format string to add.
486 * @param ... Optional format arguments of \a pszDesc to add.
487 */
488static int audioTestErrorDescAdd(PAUDIOTESTERRORDESC pErr, const char *pszDesc, ...)
489{
490 va_list va;
491 va_start(va, pszDesc);
492
493 int rc = audioTestErrorDescAddV(pErr, VERR_GENERAL_FAILURE /** @todo Fudge! */, pszDesc, va);
494
495 va_end(va);
496 return rc;
497}
498
499#if 0
500static int audioTestErrorDescAddRc(PAUDIOTESTERRORDESC pErr, int rc, const char *pszFormat, ...)
501{
502 va_list va;
503 va_start(va, pszFormat);
504
505 int rc2 = audioTestErrorDescAddV(pErr, rc, pszFormat, va);
506
507 va_end(va);
508 return rc2;
509}
510#endif
511
512/**
513 * Creates a new temporary directory with a specific (test) tag.
514 *
515 * @returns VBox status code.
516 * @param pszPath Where to return the absolute path of the created directory on success.
517 * @param cbPath Size (in bytes) of \a pszPath.
518 * @param pszTag Tag name to use for directory creation.
519 *
520 * @note Can be used multiple times with the same tag; a sub directory with an ISO time string will be used
521 * on each call.
522 */
523int AudioTestPathCreateTemp(char *pszPath, size_t cbPath, const char *pszTag)
524{
525 AssertReturn(strlen(pszTag) <= AUDIOTEST_TAG_MAX, VERR_INVALID_PARAMETER);
526
527 char szPath[RTPATH_MAX];
528
529 int rc = RTPathTemp(szPath, sizeof(szPath));
530 AssertRCReturn(rc, rc);
531 rc = AudioTestPathCreate(szPath, sizeof(szPath), pszTag);
532 AssertRCReturn(rc, rc);
533
534 return RTStrCopy(pszPath, cbPath, szPath);
535}
536
537/**
538 * Creates a new audio test set.
539 *
540 * @returns VBox status code.
541 * @param pSet Test set to create.
542 * @param pszPath Absolute path to use for the test set's temporary directory.
543 * If NULL, the OS' temporary directory will be used.
544 * @param pszTag Tag name to use for this test set.
545 */
546int AudioTestSetCreate(PAUDIOTESTSET pSet, const char *pszPath, const char *pszTag)
547{
548 AssertReturn(strlen(pszTag) <= AUDIOTEST_TAG_MAX, VERR_INVALID_PARAMETER);
549
550 int rc;
551
552 audioTestSetInitInternal(pSet);
553
554 if (pszPath)
555 {
556 rc = RTStrCopy(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszPath);
557 AssertRCReturn(rc, rc);
558
559 rc = AudioTestPathCreate(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszTag);
560 }
561 else
562 rc = AudioTestPathCreateTemp(pSet->szPathAbs, sizeof(pSet->szPathAbs), pszTag);
563 AssertRCReturn(rc, rc);
564
565 if (RT_SUCCESS(rc))
566 {
567 char szManifest[RTPATH_MAX];
568 rc = RTStrCopy(szManifest, sizeof(szManifest), pSet->szPathAbs);
569 AssertRCReturn(rc, rc);
570
571 rc = RTPathAppend(szManifest, sizeof(szManifest), AUDIOTEST_MANIFEST_FILE_STR);
572 AssertRCReturn(rc, rc);
573
574 rc = RTFileOpen(&pSet->f.hFile, szManifest,
575 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
576 AssertRCReturn(rc, rc);
577
578 rc = audioTestManifestWriteSection(pSet, "header");
579 AssertRCReturn(rc, rc);
580
581 rc = audioTestManifestWriteLn(pSet, "magic=vkat_ini"); /* VKAT Manifest, .INI-style. */
582 AssertRCReturn(rc, rc);
583 rc = audioTestManifestWriteLn(pSet, "ver=%d", AUDIOTEST_MANIFEST_VER);
584 AssertRCReturn(rc, rc);
585 rc = audioTestManifestWriteLn(pSet, "tag=%s", pszTag);
586 AssertRCReturn(rc, rc);
587
588 char szVal[64];
589 RTTIMESPEC time;
590 if (!RTTimeSpecToString(RTTimeNow(&time), szVal, sizeof(szVal)))
591 AssertFailedReturn(VERR_BUFFER_OVERFLOW);
592
593 rc = audioTestManifestWriteLn(pSet, "date_created=%s", szVal);
594 AssertRCReturn(rc, rc);
595
596 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szVal, sizeof(szVal));
597 AssertRCReturn(rc, rc);
598 rc = audioTestManifestWriteLn(pSet, "os_product=%s", szVal);
599 AssertRCReturn(rc, rc);
600 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szVal, sizeof(szVal));
601 AssertRCReturn(rc, rc);
602 rc = audioTestManifestWriteLn(pSet, "os_rel=%s", szVal);
603 AssertRCReturn(rc, rc);
604 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szVal, sizeof(szVal));
605 AssertRCReturn(rc, rc);
606 rc = audioTestManifestWriteLn(pSet, "os_ver=%s", szVal);
607 AssertRCReturn(rc, rc);
608
609 rc = audioTestManifestWriteLn(pSet, "vbox_ver=%s r%u %s (%s %s)",
610 VBOX_VERSION_STRING, RTBldCfgRevision(),
611 RTBldCfgTargetDotArch(), __DATE__, __TIME__);
612 AssertRCReturn(rc, rc);
613
614 pSet->enmMode = AUDIOTESTSETMODE_TEST;
615
616 rc = RTStrCopy(pSet->szTag, sizeof(pSet->szTag), pszTag);
617 AssertRCReturn(rc, rc);
618 }
619
620 return rc;
621}
622
623/**
624 * Destroys a test set.
625 *
626 * @returns VBox status code.
627 * @param pSet Test set to destroy.
628 */
629int AudioTestSetDestroy(PAUDIOTESTSET pSet)
630{
631 if (!pSet)
632 return VINF_SUCCESS;
633
634 int rc = VINF_SUCCESS;
635
636 PAUDIOTESTOBJ pObj, pObjNext;
637 RTListForEachSafe(&pSet->lstObj, pObj, pObjNext, AUDIOTESTOBJ, Node)
638 {
639 rc = AudioTestSetObjClose(pObj);
640 if (RT_SUCCESS(rc))
641 {
642 RTListNodeRemove(&pObj->Node);
643 RTMemFree(pObj);
644
645 Assert(pSet->cObj);
646 pSet->cObj--;
647 }
648 else
649 break;
650 }
651
652 if (RT_FAILURE(rc))
653 return rc;
654
655 Assert(pSet->cObj == 0);
656
657 if (RTFileIsValid(pSet->f.hFile))
658 {
659 RTFileClose(pSet->f.hFile);
660 pSet->f.hFile = NIL_RTFILE;
661 }
662
663 return rc;
664}
665
666/**
667 * Opens an existing audio test set.
668 *
669 * @returns VBox status code.
670 * @param pSet Test set to open.
671 * @param pszPath Absolute path of the test set to open.
672 */
673int AudioTestSetOpen(PAUDIOTESTSET pSet, const char *pszPath)
674{
675 audioTestSetInitInternal(pSet);
676
677 char szManifest[RTPATH_MAX];
678 int rc = RTStrCopy(szManifest, sizeof(szManifest), pszPath);
679 AssertRCReturn(rc, rc);
680
681 rc = RTPathAppend(szManifest, sizeof(szManifest), AUDIOTEST_MANIFEST_FILE_STR);
682 AssertRCReturn(rc, rc);
683
684 RTVFSFILE hVfsFile;
685 rc = RTVfsFileOpenNormal(szManifest, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFile);
686 if (RT_FAILURE(rc))
687 return rc;
688
689 rc = RTIniFileCreateFromVfsFile(&pSet->f.hIniFile, hVfsFile, RTINIFILE_F_READONLY);
690 RTVfsFileRelease(hVfsFile);
691 AssertRCReturn(rc, rc);
692
693 pSet->enmMode = AUDIOTESTSETMODE_VERIFY;
694
695 return rc;
696}
697
698/**
699 * Closes an opened audio test set.
700 *
701 * @param pSet Test set to close.
702 */
703void AudioTestSetClose(PAUDIOTESTSET pSet)
704{
705 AudioTestSetDestroy(pSet);
706}
707
708/**
709 * Physically wipes all related test set files off the disk.
710 *
711 * @param pSet Test set to wipe.
712 */
713void AudioTestSetWipe(PAUDIOTESTSET pSet)
714{
715 RT_NOREF(pSet);
716}
717
718/**
719 * Creates and registers a new audio test object to a test set.
720 *
721 * @returns VBox status code.
722 * @param pSet Test set to create and register new object for.
723 * @param pszName Name of new object to create.
724 * @param ppObj Where to return the pointer to the newly created object on success.
725 */
726int AudioTestSetObjCreateAndRegister(PAUDIOTESTSET pSet, const char *pszName, PAUDIOTESTOBJ *ppObj)
727{
728 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
729
730 PAUDIOTESTOBJ pObj = (PAUDIOTESTOBJ)RTMemAlloc(sizeof(AUDIOTESTOBJ));
731 AssertPtrReturn(pObj, VERR_NO_MEMORY);
732
733 int rc = RTStrPrintf(pObj->szName, sizeof(pObj->szName), "%04RU32-%s", pSet->cObj, pszName);
734 AssertRCReturn(rc, rc);
735
736 /** @todo Generalize this function more once we have more object types. */
737
738 char szFilePath[RTPATH_MAX];
739 rc = RTPathJoin(szFilePath, sizeof(szFilePath), pSet->szPathAbs, pObj->szName);
740 AssertRCReturn(rc, rc);
741
742 rc = RTFileOpen(&pObj->File.hFile, szFilePath, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
743 if (RT_SUCCESS(rc))
744 {
745 pObj->enmType = AUDIOTESTOBJTYPE_FILE;
746
747 RTListAppend(&pSet->lstObj, &pObj->Node);
748 pSet->cObj++;
749
750 *ppObj = pObj;
751 }
752
753 if (RT_FAILURE(rc))
754 RTMemFree(pObj);
755
756 return rc;
757}
758
759/**
760 * Writes to a created audio test object.
761 *
762 * @returns VBox status code.
763 * @param pObj Audio test object to write to.
764 */
765int AudioTestSetObjWrite(PAUDIOTESTOBJ pObj, void *pvBuf, size_t cbBuf)
766{
767 /** @todo Generalize this function more once we have more object types. */
768 AssertReturn(pObj->enmType == AUDIOTESTOBJTYPE_FILE, VERR_INVALID_PARAMETER);
769
770 return RTFileWrite(pObj->File.hFile, pvBuf, cbBuf, NULL);
771}
772
773/**
774 * Closes an opened audio test object.
775 *
776 * @returns VBox status code.
777 * @param pObj Audio test object to close.
778 */
779int AudioTestSetObjClose(PAUDIOTESTOBJ pObj)
780{
781 if (!pObj)
782 return VINF_SUCCESS;
783
784 /** @todo Generalize this function more once we have more object types. */
785 AssertReturn(pObj->enmType == AUDIOTESTOBJTYPE_FILE, VERR_INVALID_PARAMETER);
786
787 int rc = VINF_SUCCESS;
788
789 if (RTFileIsValid(pObj->File.hFile))
790 {
791 rc = RTFileClose(pObj->File.hFile);
792 pObj->File.hFile = NIL_RTFILE;
793 }
794
795 return rc;
796}
797
798/**
799 * Packs an audio test so that it's ready for transmission.
800 *
801 * @returns VBox status code.
802 * @param pSet Test set to pack.
803 * @param pszOutDir Directory where to store the packed test set.
804 * @param pszFileName Where to return the final name of the packed test set. Optional and can be NULL.
805 * @param cbFileName Size (in bytes) of \a pszFileName.
806 */
807int AudioTestSetPack(PAUDIOTESTSET pSet, const char *pszOutDir, char *pszFileName, size_t cbFileName)
808{
809 AssertReturn(!pszFileName || cbFileName, VERR_INVALID_PARAMETER);
810 AssertReturn(audioTestManifestIsOpen(pSet), VERR_WRONG_ORDER);
811
812 /** @todo Check and deny if \a pszOutDir is part of the set's path. */
813
814 char szOutName[RT_ELEMENTS(AUDIOTEST_PATH_PREFIX_STR) + AUDIOTEST_TAG_MAX + 16];
815 int rc = RTStrPrintf(szOutName, sizeof(szOutName), "%s-%s.tar.gz", AUDIOTEST_PATH_PREFIX_STR, pSet->szTag);
816 AssertRCReturn(rc, rc);
817
818 char szOutPath[RTPATH_MAX];
819 rc = RTPathJoin(szOutPath, sizeof(szOutPath), pszOutDir, szOutName);
820 AssertRCReturn(rc, rc);
821
822 const char *apszArgs[10];
823 unsigned cArgs = 0;
824
825 apszArgs[cArgs++] = "AudioTest";
826 apszArgs[cArgs++] = "--create";
827 apszArgs[cArgs++] = "--gzip";
828 apszArgs[cArgs++] = "--directory";
829 apszArgs[cArgs++] = pSet->szPathAbs;
830 apszArgs[cArgs++] = "--file";
831 apszArgs[cArgs++] = szOutPath;
832 apszArgs[cArgs++] = ".";
833
834 RTEXITCODE rcExit = RTZipTarCmd(cArgs, (char **)apszArgs);
835 if (rcExit != RTEXITCODE_SUCCESS)
836 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
837
838 if (RT_SUCCESS(rc))
839 {
840 if (pszFileName)
841 rc = RTStrCopy(pszFileName, cbFileName, szOutPath);
842 }
843
844 return rc;
845}
846
847/**
848 * Unpacks a formerly packed audio test set.
849 *
850 * @returns VBox status code.
851 * @param pszFile Test set file to unpack.
852 * @param pszOutDir Directory where to unpack the test set into.
853 * If the directory does not exist it will be created.
854 */
855int AudioTestSetUnpack(const char *pszFile, const char *pszOutDir)
856{
857 RT_NOREF(pszFile, pszOutDir);
858
859 // RTZipTarCmd()
860
861 return VERR_NOT_IMPLEMENTED;
862}
863
864/**
865 * Verifies an opened audio test set.
866 *
867 * @returns VBox status code.
868 * @param pSet Test set to verify.
869 * @param pszTag Tag to use for verification purpose.
870 * @param pErrDesc Where to return the test verification errors.
871 *
872 * @note Test verification errors have to be checked for errors, regardless of the
873 * actual return code.
874 */
875int AudioTestSetVerify(PAUDIOTESTSET pSet, const char *pszTag, PAUDIOTESTERRORDESC pErrDesc)
876{
877 AssertReturn(audioTestManifestIsOpen(pSet), VERR_WRONG_ORDER);
878
879 /* We ASSUME the caller has not init'd pErrDesc. */
880 audioTestErrorDescInit(pErrDesc);
881
882 char szVal[_1K]; /** @todo Enough, too much? */
883
884 int rc2 = RTIniFileQueryValue(pSet->f.hIniFile, AUDIOTEST_INI_SEC_HDR_STR, "tag", szVal, sizeof(szVal), NULL);
885 if ( RT_FAILURE(rc2)
886 || RTStrICmp(pszTag, szVal))
887 audioTestErrorDescAdd(pErrDesc, "Tag '%s' does not match with manifest's tag '%s'", pszTag, szVal);
888
889 /* Only return critical stuff not related to actual testing here. */
890 return VINF_SUCCESS;
891}
892
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette