Files
wehub-resource-sync ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:58 +08:00

130 lines
4.0 KiB
Python

# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest
import torch
from nemo.collections.asr.parts.utils.asr_multispeaker_utils import (
find_first_nonzero,
get_hidden_length_from_sample_length,
read_rttm_supervisions_lenient,
)
def _write_rttm(path, lines):
path.write_text("\n".join(lines) + "\n")
return path
@pytest.mark.unit
@pytest.mark.parametrize(
"line",
[
"SPEAKER rec1 1 1.25 2.50 <NA> <NA> speaker_A <NA>",
"SPEAKER rec1 1 1.25 2.50 <NA> <NA> speaker_A <NA> <NA>",
],
)
def test_read_rttm_supervisions_lenient_accepts_9_and_10_column_lines(tmp_path, line):
rttm_path = _write_rttm(tmp_path / "valid.rttm", [line])
supervisions = read_rttm_supervisions_lenient(rttm_path)
assert len(supervisions) == 1
segment = supervisions[0]
assert segment.id == "rec1-000000"
assert segment.recording_id == "rec1"
assert segment.channel == 1
assert segment.start == pytest.approx(1.25)
assert segment.duration == pytest.approx(2.50)
assert segment.speaker == "speaker_A"
@pytest.mark.unit
@pytest.mark.parametrize(
"line",
[
"SPEAKER rec1 1 0.0 1.0 <NA> <NA>",
"SPEAKER rec1 1 0.0 1.0",
"too short",
],
)
def test_read_rttm_supervisions_lenient_rejects_short_lines(tmp_path, line):
rttm_path = _write_rttm(tmp_path / "invalid.rttm", [line])
with pytest.raises(ValueError, match="Invalid RTTM line"):
read_rttm_supervisions_lenient(rttm_path)
@pytest.mark.unit
def test_read_rttm_supervisions_lenient_skips_blank_and_zero_duration_lines(tmp_path):
rttm_path = _write_rttm(
tmp_path / "skip.rttm",
[
"",
"SPEAKER rec1 1 0.00 0.00 <NA> <NA> speaker_A <NA>",
"SPEAKER rec1 1 3.00 1.25 <NA> <NA> speaker_B <NA>",
],
)
supervisions = read_rttm_supervisions_lenient(rttm_path)
assert len(supervisions) == 1
segment = supervisions[0]
assert segment.id == "rec1-000002"
assert segment.start == pytest.approx(3.00)
assert segment.duration == pytest.approx(1.25)
assert segment.speaker == "speaker_B"
@pytest.mark.unit
def test_read_rttm_supervisions_lenient_accepts_multiple_files(tmp_path):
rttm_path_a = _write_rttm(tmp_path / "a.rttm", ["SPEAKER rec_a 1 0.00 1.00 <NA> <NA> speaker_A <NA>"])
rttm_path_b = _write_rttm(tmp_path / "b.rttm", ["SPEAKER rec_b 2 1.00 2.00 <NA> <NA> speaker_B <NA>"])
supervisions = read_rttm_supervisions_lenient([rttm_path_a, rttm_path_b])
assert len(supervisions) == 2
assert [segment.recording_id for segment in supervisions] == ["rec_a", "rec_b"]
assert [segment.channel for segment in supervisions] == [1, 2]
assert [segment.speaker for segment in supervisions] == ["speaker_A", "speaker_B"]
@pytest.mark.unit
@pytest.mark.parametrize(
("num_samples", "expected_hidden_length"),
[
(0, 0),
(1, 1),
(1280, 1),
(1281, 2),
],
)
def test_get_hidden_length_rounds_up_to_encoder_frames(num_samples, expected_hidden_length):
assert get_hidden_length_from_sample_length(num_samples) == expected_hidden_length
@pytest.mark.unit
def test_find_first_nonzero_returns_first_threshold_crossing_or_cap():
mat = torch.tensor(
[
[0.0, 0.2, 0.6],
[0.0, 0.0, 0.0],
[0.7, 0.8, 0.0],
]
)
result = find_first_nonzero(mat, max_cap_val=99, thres=0.5)
assert torch.equal(result, torch.tensor([2, 99, 0]))