Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 0a7a700b authored by Rafael Tonholo's avatar Rafael Tonholo Committed by GitHub
Browse files

Merge pull request #9299 from shamim-emon/fix-issue-9298

Convert SearchResponse class from Java to Kotlin
parents cef409d8 ae78ffff
Loading
Loading
Loading
Loading
+0 −50
Original line number Original line Diff line number Diff line
package com.fsck.k9.mail.store.imap;


import java.util.ArrayList;
import java.util.List;

import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase;


class SearchResponse {
    private final List<Long> numbers;


    private SearchResponse(List<Long> numbers) {
        this.numbers = numbers;
    }

    public static SearchResponse parse(List<ImapResponse> responses) {
        List<Long> numbers = new ArrayList<>();

        for (ImapResponse response : responses) {
            parseSingleLine(response, numbers);
        }

        return new SearchResponse(numbers);
    }

    private static void parseSingleLine(ImapResponse response, List<Long> numbers) {
        if (response.isTagged() || response.size() < 2 || !equalsIgnoreCase(response.get(0), Responses.SEARCH)) {
            return;
        }

        int end = response.size();
        for (int i = 1; i < end; i++) {
            try {
                long number = response.getLong(i);
                numbers.add(number);
            } catch (NumberFormatException e) {
                return;
            }
        }
    }

    /**
     * @return A mutable list of numbers from the SEARCH response(s).
     */
    public List<Long> getNumbers() {
        return numbers;
    }
}
+43 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.mail.store.imap

internal class SearchResponse private constructor(
    /**
     * @return A list of numbers from the SEARCH response(s).
     */
    val numbers: List<Long>,
) {
    companion object {
        @JvmStatic
        fun parse(responses: List<ImapResponse>): SearchResponse {
            val numbers = mutableListOf<Long>()

            for (response in responses) {
                parseSingleLine(response, numbers)
            }

            return SearchResponse(numbers)
        }

        private fun parseSingleLine(response: ImapResponse, numbers: MutableList<Long>) {
            if (response.isTagged ||
                response.size < 2 ||
                !ImapResponseParser.equalsIgnoreCase(
                    response[0],
                    Responses.SEARCH,
                )
            ) {
                return
            }

            val end = response.size
            for (i in 1..<end) {
                try {
                    val number = response.getLong(i)
                    numbers.add(number)
                } catch (_: NumberFormatException) {
                    return
                }
            }
        }
    }
}
+0 −89
Original line number Original line Diff line number Diff line
package com.fsck.k9.mail.store.imap;


import java.util.Collections;
import java.util.List;

import org.junit.Test;

import static com.fsck.k9.mail.store.imap.ImapResponseHelper.createImapResponseList;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;


public class SearchResponseTest {
    @Test
    public void parse_withSingleSearchResponse_shouldExtractNumbers() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList(
                "* SEARCH 1 2 3",
                "* 23 EXISTS",
                "* SEARCH 4",
                "1 OK SEARCH completed");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(asList(1L, 2L, 3L, 4L), result.getNumbers());
    }

    @Test
    public void parse_withMultipleSearchResponses_shouldExtractNumbers() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList(
                "* SEARCH 1 2 3",
                "* 23 EXISTS",
                "* SEARCH 4",
                "1 OK SEARCH completed",
                "* SEARCH 5 6",
                "* 19 EXPUNGED",
                "* SEARCH 7",
                "2 OK SEARCH completed",
                "* SEARCH 8",
                "3 OK SEARCH completed");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), result.getNumbers());
    }

    @Test
    public void parse_withSingleTaggedSearchResponse_shouldReturnEmptyList() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList("x SEARCH 7 8 9");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(Collections.emptyList(), result.getNumbers());
    }

    @Test
    public void parse_withSingleTooShortResponse_shouldReturnEmptyList() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList("* SEARCH");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(Collections.emptyList(), result.getNumbers());
    }

    @Test
    public void parse_withSingleNoSearchResponse_shouldReturnEmptyList() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList("* 23 EXPUNGE");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(Collections.emptyList(), result.getNumbers());
    }

    @Test
    public void parse_withSingleSearchResponseContainingInvalidNumber_shouldReturnEmptyList() throws Exception {
        List<ImapResponse> imapResponses = createImapResponseList("* SEARCH A");

        SearchResponse result = SearchResponse.parse(imapResponses);

        assertNotNull(result);
        assertEquals(Collections.emptyList(), result.getNumbers());
    }
}
+128 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.mail.store.imap

import assertk.all
import assertk.assertThat
import assertk.assertions.containsExactly
import assertk.assertions.isEmpty
import assertk.assertions.prop
import com.fsck.k9.mail.store.imap.SearchResponse.Companion.parse
import jdk.dynalink.linker.support.Guards.isNotNull
import kotlin.test.Test
import org.junit.Assert

class SearchResponseTest {
    @Test
    fun parse_withSingleSearchResponse_shouldExtractNumbers() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList(
            "* SEARCH 1 2 3",
            "* 23 EXISTS",
            "* SEARCH 4",
            "1 OK SEARCH completed",
        )

        // Act
        val result = parse(imapResponses)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .containsExactly(1L, 2L, 3L, 4L)
        }
    }

    @Test
    fun parse_withMultipleSearchResponses_shouldExtractNumbers() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList(
            "* SEARCH 1 2 3",
            "* 23 EXISTS",
            "* SEARCH 4",
            "1 OK SEARCH completed",
            "* SEARCH 5 6",
            "* 19 EXPUNGED",
            "* SEARCH 7",
            "2 OK SEARCH completed",
            "* SEARCH 8",
            "3 OK SEARCH completed",
        )

        // Act
        val result = parse(imapResponses)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .containsExactly(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L)
        }
    }

    @Test
    fun parse_withSingleTaggedSearchResponse_shouldReturnEmptyList() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList("x SEARCH 7 8 9")

        // Act
        val result = parse(imapResponses)

        Assert.assertNotNull(result)
        Assert.assertEquals(emptyList<Any>(), result.numbers)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .isEmpty()
        }
    }

    @Test
    fun parse_withSingleTooShortResponse_shouldReturnEmptyList() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList("* SEARCH")

        // Act
        val result = parse(imapResponses)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .isEmpty()
        }
    }

    @Test
    fun parse_withSingleNoSearchResponse_shouldReturnEmptyList() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList("* 23 EXPUNGE")

        // Act
        val result = parse(imapResponses)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .isEmpty()
        }
    }

    @Test
    fun parse_withSingleSearchResponseContainingInvalidNumber_shouldReturnEmptyList() {
        // Arrange
        val imapResponses = ImapResponseHelper.createImapResponseList("* SEARCH A")

        // Act
        val result = parse(imapResponses)

        // Assert
        assertThat(result).all {
            isNotNull()
            prop("numbers") { it.numbers }
                .isEmpty()
        }
    }
}