Repository: bitcoin/bitcoin
File: test/functional/test_runner.py (Python)

Code before:

    def done(self):
        return not (self.jobs or self.test_queue)

    def get_next(self):
        while len(self.jobs) < self.num_jobs and self.test_queue:
            # Add tests
            test = self.test_queue.popleft()
            portseed = len(self.test_queue)
            portseed_arg = ["--portseed={}".format(portseed)]
            log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
            log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
            test_argv = test.split()
            testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
            tmpdir_arg = ["--tmpdir={}".format(testdir)]
            self.jobs.append((test,
                              time.time(),
                              subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
                                               text=True,
                                               stdout=log_stdout,
                                               stderr=log_stderr),
                              testdir,
                              log_stdout,
                              log_stderr))
        if not self.jobs:
            raise IndexError('pop from empty queue')

        # Print remaining running jobs when all jobs have been started.
        if not self.test_queue:
            print("Remaining jobs: [{}]".format(", ".join(j[0] for j in self.jobs)))

        dot_count = 0
        while True:
            # Return all procs that have finished, if any. Otherwise sleep until there is one.
            time.sleep(.5)
            ret = []
            for job in self.jobs:
                (name, start_time, proc, testdir, log_out, log_err) = job
                if proc.poll() is not None:
                    log_out.seek(0), log_err.seek(0)
                    [stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]
                    log_out.close(), log_err.close()
                    skip_reason = None
                    if proc.returncode == TEST_EXIT_PASSED and stderr == "":
                        status = "Passed"
                    elif proc.returncode == TEST_EXIT_SKIPPED:
                        status = "Skipped"
                        skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1)
                    else:
                        status = "Failed"
                    self.jobs.remove(job)

Review comment (suggestion):
i don't think this is right, as explained previously. This block of code is about `self.jobs`, which *is* a list, even after the changes here.

It is just a sanity check, so a shorter way to write it would be `assert(self.jobs)  # Can not and must not be empty here`.

Code after:
        self.test_list = test_list
        self.flags = flags
        self.jobs = {}
        self.use_term_control = use_term_control

    def done(self):
        return not (self.jobs or self.test_list)

    def get_next(self):
        while len(self.jobs) < self.num_jobs and self.test_list:
            # Add tests
            test = self.test_list.popleft()
            portseed = len(self.test_list)
            portseed_arg = ["--portseed={}".format(portseed)]
            log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
            log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
            test_argv = test.split()
            testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
            tmpdir_arg = ["--tmpdir={}".format(testdir)]

            def proc_wait(task):
                task[2].wait()
                return task

            task = [
                test,
                time.time(),
                subprocess.Popen(
                    [sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
                    text=True,
                    stdout=log_stdout,
                    stderr=log_stderr,
                ),
                testdir,
                log_stdout,
                log_stderr,
            ]
            fut = self.executor.submit(proc_wait, task)
            self.jobs[fut] = test
        assert self.jobs  # Must not be empty here

        # Print remaining running jobs when all jobs have been started.
        if not self.test_list:
            print("Remaining jobs: [{}]".format(", ".join(sorted(self.jobs.values()))))

        dot_count = 0
        while True:
            # Return all procs that have finished, if any. Otherwise sleep until there is one.
            procs = futures.wait(self.jobs.keys(), timeout=.5, return_when=futures.FIRST_COMPLETED)
            self.jobs = {fut: self.jobs[fut] for fut in procs.not_done}
            ret = []