text:
import json

from six.moves import http_client
import webob

from congress.tests import base
from congress.tests import fake_wsgi

class TestVersions(base.TestCase):

    def setUp(self):
        super(TestVersions, self).setUp()

    def test_versions_list(self):
        req = webob.Request.blank('/')
        req.accept = "application/json"
        res = req.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.OK, res.status_int)
        self.assertEqual("application/json", res.content_type)
        versions = json.loads(res.body)
        expected = {
            "versions": [{
                "status": "CURRENT",
                "updated": "2013-08-12T17:42:13Z",
                "id": "v1",
                "links": [{
                    "href": "http://localhost/v1/",
                    "rel": "self"
                }]
            }]
        }
        self.assertEqual(expected, versions)

    def test_versions_choices(self):
        req = webob.Request.blank('/fake')
        req.accept = "application/json"
        res = req.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.MULTIPLE_CHOICES, res.status_int)
        self.assertEqual("application/json", res.content_type)
        versions = json.loads(res.body)
        expected = {
            "choices": [{
                "status": "CURRENT",
                "updated": "2013-08-12T17:42:13Z",
                "id": "v1",
                "links": [{
                    "href": "http://localhost/v1/fake",
                    "rel": "self"
                }]
            }]
        }
        self.assertEqual(expected, versions)

    def test_version_v1_show(self):
        req = webob.Request.blank('/v1')
        req.accept = "application/json"
        res = req.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.OK, res.status_int)
        self.assertEqual("application/json", res.content_type)
        versions = json.loads(res.body)
        expected = {
            "version": {
                "status": "CURRENT",
                "updated": "2013-08-12T17:42:13Z",
                "id": "v1",
                "links": [{
                    "href": "http://localhost/v1/",
                    "rel": "self"
                }, {
                    "rel": "describedby",
                    "type": "text/html",
                    "href": "http://congress.readthedocs.org/",
                }]
            }
        }
        self.assertEqual(expected, versions)

    def test_version_v1_multiple_path(self):
        req = webob.Request.blank('/v1')
        req.accept = "application/json"
        res = req.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.OK, res.status_int)
        self.assertEqual("application/json", res.content_type)

        req_ = webob.Request.blank('/v1/')
        req_.accept = "application/json"
        res_ = req_.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.OK, res_.status_int)
        self.assertEqual("application/json", res_.content_type)

        self.assertEqual(json.loads(res.body), json.loads(res_.body))

    def test_version_v1_not_found(self):
        req = webob.Request.blank('/v1/fake')
        req.accept = "application/json"
        res = req.get_response(fake_wsgi.wsgi_app())
        self.assertEqual(http_client.NOT_FOUND, res.status_int)
        self.assertEqual("application/json", res.content_type)


text:
<resources>
    <string name="app_name">AbstractFactory</string>
</resources>

redpajama_set_name: RedPajamaGithub


text:
@class ViewRevealingVerticalPanHandler;
enum class ViewRevealState;

// Coordinator for the thumb strip, which is a 1-row horizontal display of tab
// miniatures above the toolbar.
@interface ThumbStripCoordinator : ChromeCoordinator

- (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                   browser:(Browser*)browser
                              initialState:(ViewRevealState)initialState
    NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                   browser:(Browser*)browser NS_UNAVAILABLE;

// The thumb strip's pan gesture handler.
@property(nonatomic, strong) ViewRevealingVerticalPanHandler* panHandler;

// The regular browser used to observe scroll events to show/hide the thumb
// strip.
@property(nonatomic, assign) Browser* regularBrowser;
// The incognito browser used to observe scroll events to show/hide the thumb
// strip.
@property(nonatomic, assign) Browser* incognitoBrowser;

@end

#endif  // IOS_CHROME_BROWSER_UI_THUMB_STRIP_THUMB_STRIP_COORDINATOR_H_


text:
package org.redmine.ta;

import org.junit.*;
import org.redmine.ta.RedmineManager.INCLUDE;
import org.redmine.ta.beans.*;
import org.redmine.ta.internal.logging.Logger;
import org.redmine.ta.internal.logging.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;

import static org.junit.Assert.*;

/**
 * This class and its dependencies are located in org.redmine.ta.api project.
 */
public class RedmineManagerTest {

    // TODO We don't know activities IDs!
    // see feature request http://www.redmine.org/issues/7506
    private static final Integer ACTIVITY_ID = 8;

    private static final Logger logger = LoggerFactory.getLogger(RedmineManagerTest.class);

    private static RedmineManager mgr;

    private static String projectKey;
    private static TestConfig testConfig;

    @BeforeClass
    public static void oneTimeSetUp() {
        testConfig = new TestConfig();
        logger.info("Running redmine tests using: " + testConfig.getURI());
//		mgr = new RedmineManager(TestConfig.getURI(), TestConfig.getApiKey());
        mgr = new RedmineManager(testConfig.getURI());
        mgr.setLogin(testConfig.getLogin());
        mgr.setPassword(testConfig.getPassword());
        Project junitTestProject = new Project();
        junitTestProject.setName("test project");
        junitTestProject.setIdentifier("test"
                + Calendar.getInstance().getTimeInMillis());

        try {
            Project createdProject = mgr.createProject(junitTestProject);
            projectKey = createdProject.getIdentifier();
        } catch (Exception e) {
            logger.error(e, "Exception while creating test project");
            Assert.fail("can't create a test project. " + e.getMessage());
        }
    }

    @AfterClass
    public static void oneTimeTearDown() {
        try {
            if (mgr != null && projectKey != null) {
                mgr.deleteProject(projectKey);
            }
        } catch (Exception e) {
            logger.error(e, "Exception while deleting test project");
            Assert.fail("can't delete the test project '" + projectKey + ". reason: "
                    + e.getMessage());
        }
    }

    @Test
    public void testCreateIssue() {
        try {
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("test zzx");

            Calendar startCal = Calendar.getInstance();
            // have to clear them because they are ignored by Redmine and prevent from comparison later
            startCal.clear(Calendar.HOUR_OF_DAY);
            startCal.clear(Calendar.MINUTE);
            startCal.clear(Calendar.SECOND);
            startCal.clear(Calendar.MILLISECOND);

            startCal.add(Calendar.DATE, 5);
            issueToCreate.setStartDate(startCal.getTime());

            Calendar due = Calendar.getInstance();
            due.add(Calendar.MONTH, 1);
            issueToCreate.setDueDate(due.getTime());
            User assignee = getOurUser();
            issueToCreate.setAssignee(assignee);

            String description = "This is the description for the new task." +
                    "\nIt has several lines." +
                    "\nThis is the last line.";
            issueToCreate.setDescription(description);

            float estimatedHours = 44;
            issueToCreate.setEstimatedHours(estimatedHours);

            Issue newIssue = mgr.createIssue(projectKey, issueToCreate);
//			System.out.println("created: " + newIssue);
            Assert.assertNotNull("Checking returned result", newIssue);
            Assert.assertNotNull("New issue must have some ID", newIssue.getId());

            // check startDate
            Calendar returnedStartCal = Calendar.getInstance();
            returnedStartCal.setTime(newIssue.getStartDate());

            Assert.assertEquals(startCal.get(Calendar.YEAR), returnedStartCal.get(Calendar.YEAR));
            Assert.assertEquals(startCal.get(Calendar.MONTH), returnedStartCal.get(Calendar.MONTH));
            Assert.assertEquals(startCal.get(Calendar.DAY_OF_MONTH), returnedStartCal.get(Calendar.DAY_OF_MONTH));

            // check dueDate
            Calendar returnedDueCal = Calendar.getInstance();
            returnedDueCal.setTime(newIssue.getDueDate());

            Assert.assertEquals(due.get(Calendar.YEAR), returnedDueCal.get(Calendar.YEAR));
            Assert.assertEquals(due.get(Calendar.MONTH), returnedDueCal.get(Calendar.MONTH));
            Assert.assertEquals(due.get(Calendar.DAY_OF_MONTH), returnedDueCal.get(Calendar.DAY_OF_MONTH));

            // check ASSIGNEE
            User actualAssignee = newIssue.getAssignee();
            Assert.assertNotNull("Checking assignee not null", actualAssignee);
            Assert.assertEquals("Checking assignee id", assignee.getId(),
                    actualAssignee.getId());

            // check AUTHOR
            Integer EXPECTED_AUTHOR_ID = getOurUser().getId();
            Assert.assertEquals(EXPECTED_AUTHOR_ID, newIssue.getAuthor().getId());

            // check ESTIMATED TIME
            Assert.assertEquals((Float) estimatedHours, newIssue.getEstimatedHours());

            // check multi-line DESCRIPTION
            String regexpStripExtra = "\\r|\\n|\\s";
            description = description.replaceAll(regexpStripExtra, "");
            String actualDescription = newIssue.getDescription();
            actualDescription = actualDescription.replaceAll(regexpStripExtra, "");
            Assert.assertEquals(description, actualDescription);

            // PRIORITY
            Assert.assertNotNull(newIssue.getPriorityId());
            Assert.assertTrue(newIssue.getPriorityId() > 0);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test
    public void testCreateIssueWithParent() {
        try {
            Issue parentIssue = new Issue();
            parentIssue.setSubject("parent 1");
            Issue newParentIssue = mgr.createIssue(projectKey, parentIssue);
            logger.debug("created parent: " + newParentIssue);

            Assert.assertNotNull("Checking parent was created", newParentIssue);
            Assert.assertNotNull("Checking ID of parent issue is not null",
                    newParentIssue.getId());

            // Integer parentId = 46;
            Integer parentId = newParentIssue.getId();

            Issue childIssue = new Issue();
            childIssue.setSubject("child 1");
            childIssue.setParentId(parentId);

            Issue newChildIssue = mgr.createIssue(projectKey, childIssue);
            logger.debug("created child: " + newChildIssue);

            Assert.assertEquals("Checking parent ID of the child issue", parentId,
                    newChildIssue.getParentId());

        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test
    public void testStartDateNull() {
        try {
            Issue issue = new Issue();
            issue.setSubject("test start date");
            issue.setStartDate(null);

            Issue newIssue = mgr.createIssue(projectKey, issue);

            Issue loadedIssue = mgr.getIssueById(newIssue.getId());
            Assert.assertNull(loadedIssue.getStartDate());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test
    public void testGetIssuesBySummary() {
        String summary = "issue with subject ABC";
        try {
            Issue issue = new Issue();
            issue.setSubject(summary);
            User assignee = getOurUser();
            issue.setAssignee(assignee);

            Issue newIssue = mgr.createIssue(projectKey, issue);
            logger.debug("created: " + newIssue);
            Assert.assertNotNull("Checking returned result", newIssue);
            Assert.assertNotNull("New issue must have some ID", newIssue.getId());

            // try to find the issue
            List<Issue> foundIssues = mgr.getIssuesBySummary(projectKey,
                    summary);

            Assert.assertNotNull("Checking if search results is not NULL", foundIssues);
            Assert.assertTrue("Search results must be not empty",
                    !(foundIssues.isEmpty()));

            Issue loadedIssue1 = RedmineTestUtils.findIssueInList(foundIssues, newIssue.getId());
            Assert.assertNotNull(loadedIssue1);
            Assert.assertEquals(summary, loadedIssue1.getSubject());

            // User actualAssignee = newIssue.getAssignee();

            // assertNotNull("Checking assignee not null", actualAssignee);
            // assertEquals("Checking assignee Name", assignee.getName(),
            // actualAssignee.getName());
            // assertEquals("Checking assignee Id", assignee.getId(),
            // actualAssignee.getId());

        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test
    public void testTryFindNonExistingIssue() {
        String summary = "some summary here for issue which does not exist";
        try {
            // try to find the issue
            List<Issue> foundIssues = mgr.getIssuesBySummary(projectKey,
                    summary);
            Assert.assertNotNull("Search result must be not null", foundIssues);
            Assert.assertTrue("Search result list must be empty",
                    foundIssues.isEmpty());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    private static User getOurUser() {
        Integer userId = Integer
                .parseInt(testConfig.getParam("createissue.userid"));
        String login = testConfig.getLogin();
        String fName = testConfig.getParam("userFName");
        String lName = testConfig.getParam("userLName");
        User user = new User();
        user.setId(userId);
        user.setLogin(login);
        user.setFirstName(fName);
        user.setLastName(lName);
        return user;
    }

    @Test(expected = IllegalArgumentException.class)
    public void testNULLHostParameter() {
        new RedmineManager(null);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testEmptyHostParameter() throws RuntimeException {
        new RedmineManager("");
    }

    @Test(expected = RedmineAuthenticationException.class)
    public void noAPIKeyOnCreateIssueThrowsAE() throws Exception {
        RedmineManager redmineMgrEmpty = new RedmineManager(testConfig.getURI());
        Issue issue = new Issue();
        issue.setSubject("test zzx");
        redmineMgrEmpty.createIssue(projectKey, issue);
    }

    @Test(expected = RedmineAuthenticationException.class)
    public void wrongAPIKeyOnCreateIssueThrowsAE() throws Exception {
        RedmineManager redmineMgrInvalidKey = new RedmineManager(testConfig.getURI(), "wrong_key");
        Issue issue = new Issue();
        issue.setSubject("test zzx");
        redmineMgrInvalidKey.createIssue(projectKey, issue);
    }

    @Test
    public void testUpdateIssue() {
        try {
            Issue issue = new Issue();
            String originalSubject = "Issue " + new Date();
            issue.setSubject(originalSubject);

            Issue newIssue = mgr.createIssue(projectKey, issue);
            String changedSubject = "changed subject";
            newIssue.setSubject(changedSubject);

            mgr.update(newIssue);

            Issue reloadedFromRedmineIssue = mgr.getIssueById(newIssue.getId());

            Assert.assertEquals(
                    "Checking if 'update issue' operation changed the 'subject' field",
                    changedSubject, reloadedFromRedmineIssue.getSubject());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    /**
     * Tests the retrieval of an {@link Issue} by its ID.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetIssueById() throws RedmineException {
        Issue issue = new Issue();
        String originalSubject = "Issue " + new Date();
        issue.setSubject(originalSubject);

        Issue newIssue = mgr.createIssue(projectKey, issue);

        Issue reloadedFromRedmineIssue = mgr.getIssueById(newIssue.getId());

        Assert.assertEquals(
                "Checking if 'get issue by ID' operation returned issue with same 'subject' field",
                originalSubject, reloadedFromRedmineIssue.getSubject());
        Tracker tracker = reloadedFromRedmineIssue.getTracker();
        Assert.assertNotNull("Tracker of issue should not be null", tracker);
        Assert.assertNotNull("ID of tracker of issue should not be null", tracker.getId());
        Assert.assertNotNull("Name of tracker of issue should not be null", tracker.getName());
    }

    /**
     * Tests the retrieval of {@link Project}s.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetProjects() throws RedmineException {
        // retrieve projects
        List<Project> projects = mgr.getProjects();
        // asserts
        Assert.assertTrue(projects.size() > 0);
        boolean found = false;
        for (Project project : projects) {
            if (project.getIdentifier().equals(projectKey)) {
                found = true;
                break;
            }
        }
        if (!found) {
            Assert.fail("Our project with key '" + projectKey + "' is not found on the server");
        }
    }

    @Test
    public void testGetIssues() {
        try {
            // create at least 1 issue
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("testGetIssues: " + new Date());
            Issue newIssue = mgr.createIssue(projectKey, issueToCreate);

            List<Issue> issues = mgr.getIssues(projectKey, null);
            logger.debug("getIssues() loaded " + issues.size() + " issues");//using query #" + queryIdIssuesCreatedLast2Days);
            Assert.assertTrue(issues.size() > 0);
            boolean found = false;
            for (Issue issue : issues) {
                if (issue.getId().equals(newIssue.getId())) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                Assert.fail("getIssues() didn't return the issue we just created. The query "
                        + " must have returned all issues created during the last 2 days");
            }
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    @Test(expected = NotFoundException.class)
    public void testGetIssuesInvalidQueryId() throws RedmineException {
        Integer invalidQueryId = 9999999;
        mgr.getIssues(projectKey, invalidQueryId);
    }

    @Test
    public void testCreateProject() throws RedmineException {
        Project projectToCreate = generateRandomProject();
        String key = null;
        try {
            Project createdProject = mgr.createProject(projectToCreate);
            key = createdProject.getIdentifier();

            Assert.assertNotNull("checking that a non-null project is returned", createdProject);

            Assert.assertEquals(projectToCreate.getIdentifier(), createdProject.getIdentifier());
            Assert.assertEquals(projectToCreate.getName(), createdProject.getName());
            Assert.assertEquals(projectToCreate.getDescription(), createdProject.getDescription());
            Assert.assertEquals(projectToCreate.getHomepage(), createdProject.getHomepage());

            List<Tracker> trackers = createdProject.getTrackers();
            Assert.assertNotNull("checking that project has some trackers", trackers);
            Assert.assertTrue("checking that project has some trackers", !(trackers.isEmpty()));
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        } finally {
            if (key != null) {
                mgr.deleteProject(key);
            }
        }
    }

    @Test
    public void testCreateGetUpdateDeleteProject() throws RedmineException {
        Project projectToCreate = generateRandomProject();
        String key = null;
        try {
            projectToCreate.setIdentifier("id" + new Date().getTime());
            logger.debug("trying to create a project with id " + projectToCreate.getIdentifier());
            Project createdProject = mgr.createProject(projectToCreate);
            key = createdProject.getIdentifier();
            String newDescr = "NEW123";
            String newName = "new name here";

            createdProject.setName(newName);
            createdProject.setDescription(newDescr);
            mgr.update(createdProject);

            Project updatedProject = mgr.getProjectByKey(key);
            Assert.assertNotNull(updatedProject);

            Assert.assertEquals(createdProject.getIdentifier(), updatedProject.getIdentifier());
            Assert.assertEquals(newName, updatedProject.getName());
            Assert.assertEquals(newDescr, updatedProject.getDescription());
            List<Tracker> trackers = updatedProject.getTrackers();
            Assert.assertNotNull("checking that project has some trackers", trackers);
            Assert.assertTrue("checking that project has some trackers", !(trackers.isEmpty()));

        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        } finally {
            if (key != null) {
                mgr.deleteProject(key);
            }
        }
    }

    @Test
    public void testCreateProjectFailsWithReservedIdentifier() throws Exception {
        Project projectToCreate = new Project();
        projectToCreate.setName("new");
        projectToCreate.setIdentifier("new");
        String key = null;
        try {
            Project createdProject = mgr.createProject(projectToCreate);
            // in case if the creation haven't failed (although it should have had!),
            // need to cleanup - delete this project
            key = createdProject.getIdentifier();

        } catch (RedmineProcessingException e) {
            Assert.assertNotNull(e.getErrors());
            Assert.assertEquals(1, e.getErrors().size());
            Assert.assertEquals("Identifier is reserved", e.getErrors().get(0));
        } finally {
            if (key != null) {
                mgr.deleteProject(key);
            }
        }
    }

    private static Project generateRandomProject() {
        Project project = new Project();
        Long timeStamp = Calendar.getInstance().getTimeInMillis();
        String key = "projkey" + timeStamp;
        String name = "project number " + timeStamp;
        String description = "some description for the project";
        project.setIdentifier(key);
        project.setName(name);
        project.setDescription(description);
        project.setHomepage("www.randompage" + timeStamp + ".com");
        return project;
    }

    @Test
    public void testCreateIssueNonUnicodeSymbols() {
        try {
            String nonLatinSymbols = "Example with accents A��o";
            Issue toCreate = new Issue();
            toCreate.setSubject(nonLatinSymbols);
            Issue created = mgr.createIssue(projectKey, toCreate);
            Assert.assertEquals(nonLatinSymbols, created.getSubject());
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }

    @Test
    public void testCreateIssueSummaryOnly() {
        try {
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("This is the summary line 123");

            Issue newIssue = mgr.createIssue(projectKey, issueToCreate);
            Assert.assertNotNull("Checking returned result", newIssue);
            Assert.assertNotNull("New issue must have some ID", newIssue.getId());

            // check AUTHOR
            Integer EXPECTED_AUTHOR_ID = getOurUser().getId();
            Assert.assertEquals(EXPECTED_AUTHOR_ID, newIssue.getAuthor().getId());

        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test(expected = NotFoundException.class)
    public void testCreateIssueInvalidProjectKey() throws RedmineException {
        Issue issueToCreate = new Issue();
        issueToCreate.setSubject("Summary line 100");
        mgr.createIssue("someNotExistingProjectKey", issueToCreate);
    }

    @Test(expected = NotFoundException.class)
    public void testGetProjectNonExistingId() throws RedmineException {
        mgr.getProjectByKey("some-non-existing-key");
    }

    @Test(expected = NotFoundException.class)
    public void testDeleteNonExistingProject() throws RedmineException {
        mgr.deleteProject("some-non-existing-key");
    }

    @Test(expected = NotFoundException.class)
    public void testGetIssueNonExistingId() throws RedmineException {
        int someNonExistingID = 999999;
        mgr.getIssueById(someNonExistingID);
    }

    @Test(expected = NotFoundException.class)
    public void testUpdateIssueNonExistingId() throws RedmineException {
        int nonExistingId = 999999;
        Issue issue = new Issue();
        issue.setId(nonExistingId);
        mgr.update(issue);
    }

    @Test
    public void testGetUsers() {
        try {
            List<User> users = mgr.getUsers();
            Assert.assertTrue(users.size() > 0);
//			boolean found = false;
//			for (Project project : projects) {
//				if (project.getIdentifier().equals(projectKey)) {
//					found = true;
//					break;
//				}
//			}
//			if (!found) {
//				fail("Our project with key '" + projectKey+"' is not found on the server");
//			}
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    @Test
    public void testGetCurrentUser() throws RedmineException {
        User currentUser = mgr.getCurrentUser();
        Assert.assertEquals(getOurUser().getId(), currentUser.getId());
        Assert.assertEquals(getOurUser().getLogin(), currentUser.getLogin());
    }

    @Test
    public void testGetUserById() throws RedmineException {
        User loadedUser = mgr.getUserById(getOurUser().getId());
        Assert.assertEquals(getOurUser().getId(), loadedUser.getId());
        Assert.assertEquals(getOurUser().getLogin(), loadedUser.getLogin());
    }

    @Test(expected = NotFoundException.class)
    public void testGetUserNonExistingId() throws RedmineException {
        mgr.getUserById(999999);
    }

    @Test(expected = NotFoundException.class)
    public void testInvalidGetCurrentUser() throws RedmineException {
        RedmineManager invalidManager = new RedmineManager(testConfig.getURI() + "/INVALID");
        invalidManager.setLogin("Invalid");
        invalidManager.setPassword("Invalid");
        invalidManager.getCurrentUser();
    }

    @Test
    public void testCreateUser() throws RedmineException {
        User createdUser = null;
        try {
            User userToCreate = generateRandomUser();
            createdUser = mgr.createUser(userToCreate);

            Assert.assertNotNull("checking that a non-null project is returned", createdUser);

            Assert.assertEquals(userToCreate.getLogin(), createdUser.getLogin());
            Assert.assertEquals(userToCreate.getFirstName(), createdUser.getFirstName());
            Assert.assertEquals(userToCreate.getLastName(), createdUser.getLastName());
            Integer id = createdUser.getId();
            Assert.assertNotNull(id);

        } catch (Exception e) {
            Assert.fail(e.getMessage());
        } finally {
            if (createdUser != null) {
                mgr.deleteUser(createdUser.getId());
            }
        }
    }

    private static User generateRandomUser() {
        User user = new User();
        user.setFirstName("fname");
        user.setLastName("lname");
        long randomNumber = new Date().getTime();
        user.setLogin("login" + randomNumber);
        user.setMail("somemail" + randomNumber + "@somedomain.com");
        user.setPassword("zzzz");

        return user;
    }

    @Test
    public void testUpdateUser() throws RedmineAuthenticationException, NotFoundException {
        User userToCreate = new User();
        userToCreate.setFirstName("fname2");
        userToCreate.setLastName("lname2");
        long randomNumber = new Date().getTime();
        userToCreate.setLogin("login33" + randomNumber);
        userToCreate.setMail("email" + randomNumber + "@somedomain.com");
        userToCreate.setPassword("1234");
        try {
            User createdUser = mgr.createUser(userToCreate);
            Integer userId = createdUser.getId();
            Assert.assertNotNull("checking that a non-null project is returned", createdUser);

            String newFirstName = "fnameNEW";
            String newLastName = "lnameNEW";
            String newMail = "newmail" + randomNumber + "@asd.com";
            createdUser.setFirstName(newFirstName);
            createdUser.setLastName(newLastName);
            createdUser.setMail(newMail);

            mgr.update(createdUser);

            User updatedUser = mgr.getUserById(userId);

            Assert.assertEquals(newFirstName, updatedUser.getFirstName());
            Assert.assertEquals(newLastName, updatedUser.getLastName());
            Assert.assertEquals(newMail, updatedUser.getMail());
            Assert.assertEquals(userId, updatedUser.getId());

        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }

    @Test
    public void userCanBeDeleted() throws RedmineException {
        User user = generateRandomUser();
        User createdUser = mgr.createUser(user);
        Integer newUserId = createdUser.getId();

        try {
            mgr.deleteUser(newUserId);
        } catch (Exception e) {
            Assert.fail(e.getMessage());
        }
        try {
            mgr.getUserById(newUserId);
            fail("Must have failed with NotFoundException because we tried to delete the user");
        } catch (NotFoundException e) {
            // ignore: the user should not be found
        }
    }

    @Test(expected = NotFoundException.class)
    public void deletingNonExistingUserThrowsNFE() throws RedmineException {
        mgr.deleteUser(999999);
    }

    @Test
    public void testGetIssuesPaging() {
        try {
            // create 27 issues. default page size is 25.
            createIssues(27);
//			mgr.setObjectsPerPage(5); <-- does not work now
            List<Issue> issues = mgr.getIssues(projectKey, null);
            logger.debug("testGetIssuesPaging() loaded " + issues.size() + " issues");//using query #" + queryIdIssuesCreatedLast2Days);
            Assert.assertTrue(issues.size() > 26);

            Set<Issue> issueSet = new HashSet<Issue>(issues);
            Assert.assertEquals(issues.size(), issueSet.size());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    private List<Issue> createIssues(int issuesNumber) throws RedmineException {
        List<Issue> issues = new ArrayList<Issue>(issuesNumber);
        for (int i = 0; i < issuesNumber; i++) {
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("some issue " + i + " " + new Date());
            Issue issue = mgr.createIssue(projectKey, issueToCreate);
            issues.add(issue);
        }
        return issues;
    }

    private Issue generateRandomIssue() {
        Random r = new Random();
        Issue issue = new Issue();
        issue.setSubject("some issue " + r.nextInt() + " " + new Date());
        return issue;
    }

    @Test
    public void testProjectsAllPagesLoaded() throws RedmineException {
        int NUM = 27; // must be larger than 25, which is a default page size in Redmine
        List<Project> projects = createProjects(NUM);

        List<Project> loadedProjects = mgr.getProjects();
        Assert.assertTrue(
                "Number of projects loaded from the server must be bigger than "
                        + NUM + ", but it's " + loadedProjects.size(),
                loadedProjects.size() > NUM);

        deleteProjects(projects);
    }

    private List<Project> createProjects(int num) throws RedmineException {
        List<Project> projects = new ArrayList<Project>(num);
        for (int i = 0; i < num; i++) {
            Project projectToCreate = generateRandomProject();
            Project p = mgr.createProject(projectToCreate);
            projects.add(p);
        }
        return projects;
    }

    private void deleteProjects(List<Project> projects) throws RedmineException {
        for (Project p : projects) {
            mgr.deleteProject(p.getIdentifier());
        }
    }

    @Test
    public void testGetTimeEntries() throws RedmineException {
        List<TimeEntry> list = mgr.getTimeEntries();
        Assert.assertNotNull(list);
    }

    @Test
    public void testCreateGetTimeEntry() throws RedmineException {
        Issue issue = createIssues(1).get(0);
        Integer issueId = issue.getId();

        TimeEntry entry = new TimeEntry();
        Float hours = 11f;
        entry.setHours(hours);
        entry.setIssueId(issueId);
        // TODO We don't know activities IDs!
        // see feature request http://www.redmine.org/issues/7506
        entry.setActivityId(ACTIVITY_ID);
        TimeEntry createdEntry = mgr.createTimeEntry(entry);

        Assert.assertNotNull(createdEntry);
        logger.debug("Created time entry " + createdEntry);
        Assert.assertEquals(hours, createdEntry.getHours());

        Float newHours = 22f;
        createdEntry.setHours(newHours);

        mgr.update(createdEntry);

        TimeEntry updatedEntry = mgr.getTimeEntry(createdEntry.getId());
        Assert.assertEquals(newHours, updatedEntry.getHours());
    }

    @Test(expected = NotFoundException.class)
    public void testCreateDeleteTimeEntry() throws RedmineException {
        Issue issue = createIssues(1).get(0);
        Integer issueId = issue.getId();

        TimeEntry entry = new TimeEntry();
        Float hours = 4f;
        entry.setHours(hours);
        entry.setIssueId(issueId);
        entry.setActivityId(ACTIVITY_ID);
        TimeEntry createdEntry = mgr.createTimeEntry(entry);
        Assert.assertNotNull(createdEntry);

        mgr.deleteTimeEntry(createdEntry.getId());
        mgr.getTimeEntry(createdEntry.getId());
    }

    @Test
    public void testGetTimeEntriesForIssue() throws RedmineException {
        Issue issue = createIssues(1).get(0);
        Integer issueId = issue.getId();
        Float hours1 = 2f;
        Float hours2 = 7f;
        Float totalHoursExpected = hours1 + hours2;
        TimeEntry createdEntry1 = createTimeEntry(issueId, hours1);
        TimeEntry createdEntry2 = createTimeEntry(issueId, hours2);
        Assert.assertNotNull(createdEntry1);
        Assert.assertNotNull(createdEntry2);

        List<TimeEntry> entries = mgr.getTimeEntriesForIssue(issueId);
        Assert.assertEquals(2, entries.size());
        Float totalTime = 0f;
        for (TimeEntry timeEntry : entries) {
            totalTime += timeEntry.getHours();
        }
        Assert.assertEquals(totalHoursExpected, totalTime);
    }

    private TimeEntry createTimeEntry(Integer issueId, float hours) throws RedmineException {
        TimeEntry entry = new TimeEntry();
        entry.setHours(hours);
        entry.setIssueId(issueId);
        entry.setActivityId(ACTIVITY_ID);
        return mgr.createTimeEntry(entry);
    }

    @Test(expected = NotFoundException.class)
    public void testDeleteIssue() throws RedmineException {
        Issue issue = createIssues(1).get(0);
        Issue retrievedIssue = mgr.getIssueById(issue.getId());
        Assert.assertEquals(issue, retrievedIssue);

        mgr.deleteIssue(issue.getId());
        mgr.getIssueById(issue.getId());
    }

    @Test
    public void testUpdateIssueSpecialXMLtags() throws Exception {
        Issue issue = createIssues(1).get(0);
        String newSubject = "\"text in quotes\" and <xml> tags";
        String newDescription = "<taghere>\"abc\"</here>";
        issue.setSubject(newSubject);
        issue.setDescription(newDescription);
        mgr.update(issue);

        Issue updatedIssue = mgr.getIssueById(issue.getId());
        Assert.assertEquals(newSubject, updatedIssue.getSubject());
        Assert.assertEquals(newDescription, updatedIssue.getDescription());
    }

    /**
     * The custom fields used here MUST ALREADY EXIST on the server and be
     * associated with the required task type (bug/feature/task/..).
     * <p/>
     * See feature request http://www.redmine.org/issues/9664
     */
    @Test
    public void testCustomFields() throws Exception {
        Issue issue = createIssues(1).get(0);
        // default empty values
        Assert.assertEquals(2, issue.getCustomFields().size());

        // TODO update this!
        int id1 = 1; // TODO this is pretty much a hack, we don't generally know these ids!
        String custom1FieldName = "my_custom_1";
        String custom1Value = "some value 123";

        int id2 = 2;
        String custom2FieldName = "custom_boolean_1";
        String custom2Value = "true";

        issue.setCustomFields(new ArrayList<CustomField>());

        issue.getCustomFields().add(new CustomField(id1, custom1FieldName, custom1Value));
        issue.getCustomFields().add(new CustomField(id2, custom2FieldName, custom2Value));
        mgr.update(issue);

        Issue updatedIssue = mgr.getIssueById(issue.getId());
        Assert.assertEquals(2, updatedIssue.getCustomFields().size());
        Assert.assertEquals(custom1Value, updatedIssue.getCustomField(custom1FieldName));
        Assert.assertEquals(custom2Value, updatedIssue.getCustomField(custom2FieldName));
    }

    @Test
    public void testUpdateIssueDoesNotChangeEstimatedTime() {
        try {
            Issue issue = new Issue();
            String originalSubject = "Issue " + new Date();
            issue.setSubject(originalSubject);

            Issue newIssue = mgr.createIssue(projectKey, issue);
            Assert.assertEquals("Estimated hours must be NULL", null, newIssue.getEstimatedHours());

            mgr.update(newIssue);

            Issue reloadedFromRedmineIssue = mgr.getIssueById(newIssue.getId());
            Assert.assertEquals("Estimated hours must be NULL", null, reloadedFromRedmineIssue.getEstimatedHours());
        } catch (Exception e) {
            Assert.fail();
        }
    }

    /**
     * Tests the correct retrieval of the parent id of sub {@link Project}.
     *
     * @throws RedmineProcessingException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testSubProjectIsCreatedWithCorrectParentId() throws RedmineException {
        Project createdMainProject = null;
        try {
            createdMainProject = createProject();
            Project subProject = createSubProject(createdMainProject);
            Assert.assertEquals("Must have correct parent ID",
                    createdMainProject.getId(), subProject.getParentId());
        } finally {
            if (createdMainProject != null) {
                mgr.deleteProject(createdMainProject.getIdentifier());
            }
        }
    }

    private Project createProject() throws RedmineException {
        Project mainProject = new Project();
        long id = new Date().getTime();
        mainProject.setName("project" + id);
        mainProject.setIdentifier("project" + id);
        return mgr.createProject(mainProject);
    }

    private Project createSubProject(Project parent) throws RedmineException {
        Project project = new Project();
        long id = new Date().getTime();
        project.setName("sub_pr" + id);
        project.setIdentifier("subpr" + id);
        project.setParentId(parent.getId());
        return mgr.createProject(project);
    }

    @Test
    public void testIssueDoneRatio() {
        try {
            Issue issue = new Issue();
            String subject = "Issue " + new Date();
            issue.setSubject(subject);

            Issue createdIssue = mgr.createIssue(projectKey, issue);
            Assert.assertEquals("Initial 'done ratio' must be 0", (Integer) 0, createdIssue.getDoneRatio());
            Integer doneRatio = 50;
            createdIssue.setDoneRatio(doneRatio);
            mgr.update(createdIssue);

            Integer issueId = createdIssue.getId();
            Issue reloadedFromRedmineIssue = mgr.getIssueById(issueId);
            Assert.assertEquals(
                    "Checking if 'update issue' operation changed 'done ratio' field",
                    doneRatio, reloadedFromRedmineIssue.getDoneRatio());

            Integer invalidDoneRatio = 130;
            reloadedFromRedmineIssue.setDoneRatio(invalidDoneRatio);
            try {
                mgr.update(reloadedFromRedmineIssue);
            } catch (RedmineProcessingException e) {
                Assert.assertEquals("Must be 1 error", 1, e.getErrors().size());
                Assert.assertEquals("Checking error text", "% Done is not included in the list", e.getErrors().get(0));
            }

            Issue reloadedFromRedmineIssueUnchanged = mgr.getIssueById(issueId);
            Assert.assertEquals(
                    "'done ratio' must have remained unchanged after invalid value",
                    doneRatio, reloadedFromRedmineIssueUnchanged.getDoneRatio());
        } catch (Exception e) {
            fail(e.toString());
        }
    }

    @Test
    public void testIssueNullDescriptionDoesNotEraseIt() {
        try {
            Issue issue = new Issue();
            String subject = "Issue " + new Date();
            String descr = "Some description";
            issue.setSubject(subject);
            issue.setDescription(descr);

            Issue createdIssue = mgr.createIssue(projectKey, issue);
            Assert.assertEquals("Checking description", descr, createdIssue.getDescription());

            createdIssue.setDescription(null);
            mgr.update(createdIssue);

            Integer issueId = createdIssue.getId();
            Issue reloadedFromRedmineIssue = mgr.getIssueById(issueId);
            Assert.assertEquals(
                    "Description must not be erased",
                    descr, reloadedFromRedmineIssue.getDescription());

            reloadedFromRedmineIssue.setDescription("");
            mgr.update(reloadedFromRedmineIssue);

            Issue reloadedFromRedmineIssueUnchanged = mgr.getIssueById(issueId);
            Assert.assertEquals(
                    "Description must be erased",
                    "", reloadedFromRedmineIssueUnchanged.getDescription());
        } catch (Exception e) {
            Assert.fail();
        }
    }

    @Test
    public void testIssueJournals() {
        try {
            // create at least 1 issue
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("testGetIssues: " + new Date());
            Issue newIssue = mgr.createIssue(projectKey, issueToCreate);

            Issue loadedIssueWithJournals = mgr.getIssueById(newIssue.getId(), INCLUDE.journals);
            Assert.assertTrue(loadedIssueWithJournals.getJournals().isEmpty());

            String commentDescribingTheUpdate = "some comment describing the issue update";
            loadedIssueWithJournals.setSubject("new subject");
            loadedIssueWithJournals.setNotes(commentDescribingTheUpdate);
            mgr.update(loadedIssueWithJournals);

            Issue loadedIssueWithJournals2 = mgr.getIssueById(newIssue.getId(), INCLUDE.journals);
            Assert.assertEquals(1, loadedIssueWithJournals2.getJournals().size());

            Journal journalItem = loadedIssueWithJournals2.getJournals().get(0);
            Assert.assertEquals(commentDescribingTheUpdate, journalItem.getNotes());
            User ourUser = getOurUser();
            // can't compare User objects because either of them is not completely filled
            Assert.assertEquals(ourUser.getId(), journalItem.getUser().getId());
            Assert.assertEquals(ourUser.getFirstName(), journalItem.getUser().getFirstName());
            Assert.assertEquals(ourUser.getLastName(), journalItem.getUser().getLastName());

            Issue loadedIssueWithoutJournals = mgr.getIssueById(newIssue.getId());
            Assert.assertTrue(loadedIssueWithoutJournals.getJournals().isEmpty());

        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    @Test
    public void testCreateRelation() {
        try {
            List<Issue> issues = createIssues(2);
            Issue src = issues.get(0);
            Issue target = issues.get(1);

            String relationText = IssueRelation.TYPE.precedes.toString();
            IssueRelation r = mgr.createRelation(src.getId(), target.getId(), relationText);
            assertEquals(src.getId(), r.getIssueId());
            Assert.assertEquals(target.getId(), r.getIssueToId());
            Assert.assertEquals(relationText, r.getType());
        } catch (Exception e) {
            Assert.fail(e.toString());
        }
    }

    private IssueRelation createTwoRelatedIssues() throws RedmineException {
        List<Issue> issues = createIssues(2);
        Issue src = issues.get(0);
        Issue target = issues.get(1);

        String relationText = IssueRelation.TYPE.precedes.toString();
        return mgr.createRelation(src.getId(), target.getId(), relationText);
    }

    @Test
    public void issueRelationsAreCreatedAndLoadedOK() {
        try {
            IssueRelation relation = createTwoRelatedIssues();
            Issue issue = mgr.getIssueById(relation.getIssueId(), INCLUDE.relations);
            Issue issueTarget = mgr.getIssueById(relation.getIssueToId(), INCLUDE.relations);

            Assert.assertEquals(1, issue.getRelations().size());
            Assert.assertEquals(1, issueTarget.getRelations().size());

            IssueRelation relation1 = issue.getRelations().get(0);
            assertEquals(issue.getId(), relation1.getIssueId());
            assertEquals(issueTarget.getId(), relation1.getIssueToId());
            assertEquals("precedes", relation1.getType());
            assertEquals((Integer) 0, relation1.getDelay());

            IssueRelation reverseRelation = issueTarget.getRelations().get(0);
            // both forward and reverse relations are the same!
            Assert.assertEquals(relation1, reverseRelation);
        } catch (Exception e) {
            Assert.fail(e.toString());
        }
    }

    @Test
    public void testIssureRelationDelete() throws RedmineException {
        IssueRelation relation = createTwoRelatedIssues();

        mgr.deleteRelation(relation.getId());
        Issue issue = mgr.getIssueById(relation.getIssueId(), INCLUDE.relations);
        Assert.assertEquals(0, issue.getRelations().size());
    }

    @Test
    public void testIssueRelationsDelete() throws RedmineException {
        List<Issue> issues = createIssues(3);
        Issue src = issues.get(0);
        Issue target = issues.get(1);
        String relationText = IssueRelation.TYPE.precedes.toString();

        mgr.createRelation(src.getId(), target.getId(), relationText);

        target = issues.get(2);
        mgr.createRelation(src.getId(), target.getId(), relationText);

        src = mgr.getIssueById(src.getId(), INCLUDE.relations);
        mgr.deleteIssueRelations(src);

        Issue issue = mgr.getIssueById(src.getId(), INCLUDE.relations);
        Assert.assertEquals(0, issue.getRelations().size());
    }

    /**
     * this test is ignored because:
     * 1) we can't create Versions. see http://www.redmine.org/issues/9088
     * 2) we don't currently set versions when creating issues.
     */
    @Ignore
    @Test
    public void issueFixVersionIsSet() throws Exception {

        String existingProjectKey = "test";
        Issue toCreate = generateRandomIssue();
        Version v = new Version();
        String versionName = "1.0";
        v.setName("1.0");
        v.setId(1);
        toCreate.setTargetVersion(v);
        Issue createdIssue = mgr.createIssue(existingProjectKey, toCreate);

        Assert.assertNotNull(createdIssue.getTargetVersion());
        Assert.assertEquals(createdIssue.getTargetVersion().getName(), versionName);
    }

    // Redmine ignores this parameter for "get projects" request. see bug http://www.redmine.org/issues/8545
    @Ignore
    @Test
    public void testGetProjectsIncludesTrackers() {
        try {
            List<Project> projects = mgr.getProjects();
            Assert.assertTrue(projects.size() > 0);
            Project p1 = projects.get(0);
            Assert.assertNotNull(p1.getTrackers());
            // XXX there could be a case when a project does not have any trackers
            // need to create a project with some trackers to make this test deterministic
            Assert.assertTrue(!p1.getTrackers().isEmpty());
            logger.debug("Created trackers " + p1.getTrackers());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    @Ignore
    @Test
    public void testSpentTimeFieldLoaded() {
        try {
            Issue issue = new Issue();
            String subject = "Issue " + new Date();
            issue.setSubject(subject);
            float spentHours = 2;
            issue.setSpentHours(spentHours);

            Issue createdIssue = mgr.createIssue(projectKey, issue);
            Issue newIssue = mgr.getIssueById(createdIssue.getId());
            Assert.assertEquals((Float)spentHours, newIssue.getSpentHours());
        } catch (Exception e) {
            Assert.fail();
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void invalidTimeEntryFailsWithIAEOnCreate() throws RedmineException {
        mgr.createTimeEntry(createIncompleteTimeEntry());
    }

    @Test(expected = IllegalArgumentException.class)
    public void invalidTimeEntryFailsWithIAEOnUpdate() throws RedmineException {
        mgr.update(createIncompleteTimeEntry());
    }

    private TimeEntry createIncompleteTimeEntry() {
        TimeEntry timeEntry = new TimeEntry();
        timeEntry.setActivityId(ACTIVITY_ID);
        timeEntry.setSpentOn(new Date());
        timeEntry.setHours(1.5f);
        return timeEntry;
    }

    @Test
    public void testViolateTimeEntryConstraint_ProjectOrIssueID_issue66() throws RedmineException {
        TimeEntry timeEntry = createIncompleteTimeEntry();
        // Now can try to verify with project ID (only test with issue ID seems to be already covered)
        int projectId = mgr.getProjects().get(0).getId();
        timeEntry.setProjectId(projectId);
        try {
            TimeEntry created = mgr.createTimeEntry(timeEntry);
            logger.debug("Created time entry " + created);
        } catch (Exception e) {
            e.printStackTrace();
            fail("Unexpected " + e.getClass().getSimpleName() + ": " + e.getMessage());
        }
    }

    /**
     * tests the retrieval of statuses.
     *
     * @throws RedmineProcessingException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetStatuses() throws RedmineException {
        // TODO we should create some statuses first, but the Redmine Java API does not support this presently
        List<IssueStatus> statuses = mgr.getStatuses();
        Assert.assertFalse("Expected list of statuses not to be empty", statuses.isEmpty());
        for (IssueStatus issueStatus : statuses) {
            // asserts on status
            assertNotNull("ID of status must not be null", issueStatus.getId());
            assertNotNull("Name of status must not be null", issueStatus.getName());
        }
    }

    /**
     * tests the creation of an invalid {@link Version}.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test(expected = IllegalArgumentException.class)
    public void testCreateInvalidVersion() throws RedmineException {
        Version version = new Version(null, "Invalid test version " + UUID.randomUUID().toString());
        mgr.createVersion(version);
    }

    /**
     * tests the deletion of an invalid {@link Version}. Expects a
     * {@link NotFoundException} to be thrown.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test(expected = NotFoundException.class)
    public void testDeleteInvalidVersion() throws RedmineException {
        // create new test version
        Version version = new Version(null, "Invalid test version " + UUID.randomUUID().toString());
        version.setDescription("An invalid test version created by " + this.getClass());
        // set invalid id
        version.setId(-1);
        // now try to delete version
        mgr.deleteVersion(version);
    }

    /**
     * tests the deletion of a {@link Version}.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testDeleteVersion() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        // create new test version
        Version version = new Version(project, "Test version " + UUID.randomUUID().toString());
        version.setDescription("A test version created by " + this.getClass());
        version.setStatus("open");
        Version newVersion = mgr.createVersion(version);
        // assert new test version
        Assert.assertNotNull("Expected new version not to be null", newVersion);
        // now delete version
        mgr.deleteVersion(newVersion);
        // assert that the version is gone
        List<Version> versions = mgr.getVersions(project.getId());
        Assert.assertTrue("List of versions of test project must be empty now but is " + versions, versions.isEmpty());
    }

    /**
     * tests the retrieval of {@link Version}s.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetVersions() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        // create some versions
        Version testVersion1 = mgr.createVersion(new Version(project, "Version" + UUID.randomUUID()));
        Version testVersion2 = mgr.createVersion(new Version(project, "Version" + UUID.randomUUID()));
        try {
            List<Version> versions = mgr.getVersions(project.getId());
            Assert.assertEquals("Wrong number of versions for project " + project.getName() + " delivered by Redmine Java API", 2, versions.size());
            for (Version version : versions) {
                // assert version
                Assert.assertNotNull("ID of version must not be null", version.getId());
                Assert.assertNotNull("Name of version must not be null", version.getName());
                Assert.assertNotNull("Project of version must not be null", version.getProject());
            }
        } finally {
            if (testVersion1 != null) {
                mgr.deleteVersion(testVersion1);
            }
            if (testVersion2 != null) {
                mgr.deleteVersion(testVersion2);
            }
        }
    }

    @Ignore // see Redmine bug http://www.redmine.org/issues/10241
    @Test
    public void versionIsRetrievedById() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        Version createdVersion = mgr.createVersion(new Version(project, "Version_1_" + UUID.randomUUID()));
        Version versionById = mgr.getVersionById(createdVersion.getId());
        assertEquals(createdVersion, versionById);
    }

    @Ignore // see Redmine bug http://www.redmine.org/issues/10241
    @Test
    public void versionIsUpdated() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        Version createdVersion = mgr.createVersion(new Version(project, "Version_1_" + UUID.randomUUID()));
        String description = "new description";
        createdVersion.setDescription(description);
        mgr.update(createdVersion);
        Version versionById = mgr.getVersionById(createdVersion.getId());
        assertEquals(description, versionById.getDescription());
    }

    /**
     * tests the creation and deletion of a {@link IssueCategory}.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testCreateAndDeleteIssueCategory() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        // create new test category
        IssueCategory category = new IssueCategory(project, "Category" + new Date().getTime());
        category.setAssignee(getOurUser());
        IssueCategory newIssueCategory = mgr.createCategory(category);
        // assert new test category
        Assert.assertNotNull("Expected new category not to be null", newIssueCategory);
        Assert.assertNotNull("Expected project of new category not to be null", newIssueCategory.getProject());
        Assert.assertNotNull("Expected assignee of new category not to be null", newIssueCategory.getAssignee());
        // now delete category
        mgr.deleteCategory(newIssueCategory);
        // assert that the category is gone
        List<IssueCategory> categories = mgr.getCategories(project.getId());
        Assert.assertTrue("List of categories of test project must be empty now but is " + categories, categories.isEmpty());
    }

    /**
     * tests the retrieval of {@link IssueCategory}s.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetIssueCategories() throws RedmineException {
        Project project = mgr.getProjectByKey(projectKey);
        // create some categories
        IssueCategory testIssueCategory1 = new IssueCategory(project, "Category" + new Date().getTime());
        testIssueCategory1.setAssignee(getOurUser());
        IssueCategory newIssueCategory1 = mgr.createCategory(testIssueCategory1);
        IssueCategory testIssueCategory2 = new IssueCategory(project, "Category" + new Date().getTime());
        testIssueCategory2.setAssignee(getOurUser());
        IssueCategory newIssueCategory2 = mgr.createCategory(testIssueCategory2);
        try {
            List<IssueCategory> categories = mgr.getCategories(project.getId());
            Assert.assertEquals("Wrong number of categories for project " + project.getName() + " delivered by Redmine Java API", 2, categories.size());
            for (IssueCategory category : categories) {
                // assert category
                Assert.assertNotNull("ID of category must not be null", category.getId());
                Assert.assertNotNull("Name of category must not be null", category.getName());
                Assert.assertNotNull("Project of category must not be null", category.getProject());
                Assert.assertNotNull("Assignee of category must not be null", category.getAssignee());
            }
        } finally {
            // scrub test categories
            if (newIssueCategory1 != null) {
                mgr.deleteCategory(newIssueCategory1);
            }
            if (newIssueCategory2 != null) {
                mgr.deleteCategory(newIssueCategory2);
            }
        }
    }

    /**
     * tests the creation of an invalid {@link IssueCategory}.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test(expected = IllegalArgumentException.class)
    public void testCreateInvalidIssueCategory() throws RedmineException {
        IssueCategory category = new IssueCategory(null, "InvalidCategory" + new Date().getTime());
        mgr.createCategory(category);
    }

    /**
     * tests the deletion of an invalid {@link IssueCategory}. Expects a
     * {@link NotFoundException} to be thrown.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test(expected = NotFoundException.class)
    public void testDeleteInvalidIssueCategory() throws RedmineException {
        // create new test category
        IssueCategory category = new IssueCategory(null, "InvalidCategory" + new Date().getTime());
        // set invalid id
        category.setId(-1);
        // now try to delete category
        mgr.deleteCategory(category);
    }

    /**
     * Tests the retrieval of {@link Tracker}s.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetTrackers() throws RedmineException {
        List<Tracker> trackers = mgr.getTrackers();
        assertNotNull("List of trackers returned should not be null", trackers);
        assertFalse("List of trackers returned should not be empty", trackers.isEmpty());
        for (Tracker tracker : trackers) {
            assertNotNull("Tracker returned should not be null", tracker);
            assertNotNull("ID of tracker returned should not be null", tracker.getId());
            assertNotNull("Name of tracker returned should not be null", tracker.getName());
        }
    }

    /**
     * Tests the retrieval of an {@link Issue}, inlcuding the {@link org.redmine.ta.beans.Attachment}s.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testGetIssueWithAttachments() throws RedmineException {
        Issue newIssue = null;
        try {
            // create at least 1 issue
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("testGetIssueAttachment_" + UUID.randomUUID());
            newIssue = mgr.createIssue(projectKey, issueToCreate);
            // TODO create test attachments for the issue once the Redmine REST API allows for it
            // retrieve issue attachments
            Issue retrievedIssue = mgr.getIssueById(newIssue.getId(), INCLUDE.attachments);
            Assert.assertNotNull("List of attachments retrieved for issue " + newIssue.getId() + " delivered by Redmine Java API should not be null", retrievedIssue.getAttachments());
            // TODO assert attachments once we actually receive ones for our test issue
        } finally {
            // scrub test issue
            if (newIssue != null) {
                mgr.deleteIssue(newIssue.getId());
            }
        }
    }

    /**
     * Tests the retrieval of an {@link org.redmine.ta.beans.Attachment} by its ID.
     * TODO reactivate once the Redmine REST API allows for creating attachments
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    // @Test
    public void testGetAttachmentById() throws RedmineException {
        // TODO where do we get a valid attachment number from? We can't create an attachment by our own for the test as the Redmine REST API does not support that.
        int attachmentID = 1;
        Attachment attachment = mgr.getAttachmentById(attachmentID);
        Assert.assertNotNull("Attachment retrieved by ID " + attachmentID + " should not be null", attachment);
        Assert.assertNotNull("Content URL of attachment retrieved by ID " + attachmentID + " should not be null", attachment.getContentURL());
        // TODO more asserts on the attachment once this delivers an attachment
    }

    /**
     * Tests the download of the content of an {@link org.redmine.ta.beans.Attachment}.
     * TODO reactivate once the Redmine REST API allows for creating attachments
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    // @Test
    public void testDownloadAttachmentContent() throws RedmineException {
        // TODO where do we get a valid attachment number from? We can't create an attachment by our own for the test as the Redmine REST API does not support that.
        int attachmentID = 1;
        // retrieve issue attachment
        Attachment attachment = mgr.getAttachmentById(attachmentID);
        // download  attachment content
        byte[] attachmentContent = mgr.downloadAttachmentContent(attachment);
        Assert.assertNotNull("Download of content of attachment with content URL " + attachment.getContentURL() + " should not be null", attachmentContent);
    }

    /**
     * Tests the creation and retrieval of an  {@link org.redmine.ta.beans.Issue} with a {@link IssueCategory}.
     *
     * @throws RedmineException        thrown in case something went wrong in Redmine
     * @throws IOException             thrown in case something went wrong while performing I/O
     *                                 operations
     * @throws RedmineAuthenticationException thrown in case something went wrong while trying to login
     * @throws NotFoundException       thrown in case the objects requested for could not be found
     */
    @Test
    public void testCreateAndGetIssueWithCategory() throws RedmineException {
        IssueCategory newIssueCategory = null;
        Issue newIssue = null;
        try {
            Project project = mgr.getProjectByKey(projectKey);
            // create an issue category
            IssueCategory category = new IssueCategory(project, "Category_" + new Date().getTime());
            category.setAssignee(getOurUser());
            newIssueCategory = mgr.createCategory(category);
            // create an issue
            Issue issueToCreate = new Issue();
            issueToCreate.setSubject("testGetIssueWithCategory_" + UUID.randomUUID());
            issueToCreate.setCategory(newIssueCategory);
            newIssue = mgr.createIssue(projectKey, issueToCreate);
            // retrieve issue
            Issue retrievedIssue = mgr.getIssueById(newIssue.getId());
            // assert retrieved category of issue
            IssueCategory retrievedCategory = retrievedIssue.getCategory();
            Assert.assertNotNull("Category retrieved for issue " + newIssue.getId() + " should not be null", retrievedCategory);
            Assert.assertEquals("ID of category retrieved for issue " + newIssue.getId() + " is wrong", newIssueCategory.getId(), retrievedCategory.getId());
            Assert.assertEquals("Name of category retrieved for issue " + newIssue.getId() + " is wrong", newIssueCategory.getName(), retrievedCategory.getName());
        } finally {
            if (newIssue != null) {
                mgr.deleteIssue(newIssue.getId());
            }
            if (newIssueCategory != null) {
                mgr.deleteCategory(newIssueCategory);
            }
        }
    }

    @Test
    public void getNewsDoesNotFailForNULLProject() throws RedmineException {
        mgr.getNews(null);
    }

    @Test
    public void getNewsDoesNotFailForTempProject() throws RedmineException {
        mgr.getNews(projectKey);
    }

    @Test
    public void getSavedQueriesDoesNotFailForTempProject() throws RedmineException {
        mgr.getSavedQueries(projectKey);
    }

    @Test
    public void getSavedQueriesDoesNotFailForNULLProject() throws RedmineException {
        mgr.getSavedQueries(null);
    }
}


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
null

#### Original name
Puccinia evidens Harkn.

### Remarks
null


text:
"""
web.blueprints.helpers.log

This module provides formatter functions that normalize the different
sources of logs to a dict.  The latter represents a valid table row to
:py:cls:`LogTableExtended`.

"""
from datetime import datetime, timezone
from functools import partial

from flask import url_for

from pycroft.helpers.i18n import Message
from web.blueprints.helpers.table import datetime_format

def format_log_entry(entry, log_type):
    """Format a logentry in correct json

    :param LogEntry entry:
    :param log_type: The logtype to include, currently ``'user'`` or
        ``'room'``.
    """
    return {
        'created_at': datetime_format(entry.created_at),
        'raw_created_at': entry.created_at,
        'user': {
            'type': 'native',  # parses it as a link
            'title': entry.author.name,
            'href': url_for("user.user_show", user_id=entry.author.id)
        },
        'message': Message.from_json(entry.message).localize(),
        'type': log_type
    }

format_user_log_entry = partial(format_log_entry, log_type='user')
format_task_log_entry = partial(format_log_entry, log_type='task')
format_room_log_entry = partial(format_log_entry, log_type='room')

def radius_description(interface, entry):
    """Build a readable log message from a radius log entry.

    :param interface: Something whose string representation can be
        used to inform about the port the radius log happened.  For
        instance, this can be a :py:cls:`SwitchPort` object
        formatting itself to `switch-wu5-00 (D15)` or similar.
    :param RadiusLogEntry entry: A :py:cls:`RadiusLogEntry` as
        obtained from a :py:cls:`HadesLogs` lookup.
    """
    prefix = "{port} – {mac} – ".format(port=interface, mac=entry.mac)
    if not entry:
        msg = ("REJECTED - This should never happen!"
               " Please contact a root of your choice.")
    else:
        msg = "Groups: {}, VLANs: {}".format(", ".join(entry.groups),
                                             ", ".join(entry.vlans))
    return prefix + msg

def format_hades_log_entry(interface, entry):
    """Turn Radius Log entry information into a canonical form

    This utilizes :py:func:`radius_description` but returns a dict in
    a format conforming with other log sources' output (consider user
    and room logs)

    :param interface: See :py:func:`radius_description`.
    :param entry: See :py:func:`radius_description`.
    """
    date = entry.time
    desc = radius_description(interface, entry)
    return {
        'created_at': datetime_format(date),
        'raw_created_at': date,
        'user': {
            'type': 'plain',  # (w/o link) vs. 'native' (w/ link)
            'title': "Radius",  # or switch name?
        },
        'message': desc,
        'type': 'hades'
    }

def format_custom_hades_message(message):
    date = datetime.now(tz=timezone.utc)
    return {
        'created_at': datetime_format(date),
        'raw_created_at': date,
        'user': {
            'type': 'plain',
            'title': "Radius",
        },
        'message': message,
        'type': 'hades'
    }

_msg_disabled = ("WARNING: The HadesLogs extension is not configured properly. "
                       "Logs cannot be displayed.")
_msg_disconnected = ("None of this user's hosts (if any) are in a connected room. "
                     "Logs cannot be displayed.")
_msg_error = "WARNING: an error occurred when fetching hades logs."
_msg_timeout = "WARNING: Timeout when fetching hades logs."
format_hades_disabled = partial(format_custom_hades_message, message=_msg_disabled)
format_user_not_connected = partial(format_custom_hades_message, message=_msg_disconnected)
format_hades_error = partial(format_custom_hades_message, message=_msg_error)
format_hades_timeout = partial(format_custom_hades_message, message=_msg_timeout)


text:
* [Intro](#intro)
* [Screenshots](#screenshots)
* [Installation](#installation)
* [Building from Source](#building-from-source)
* [Limitations](#limitations)

## Intro

Timberlake is a Go server paired with a React.js frontend. It improves on
existing Hadoop job trackers by providing a lightweight realtime view of your
running and finished MapReduce jobs. Timberlake exposes the counters and
configuration that are the most useful, allowing you to get a quick overview of
the whole cluster or dig into the performance and behavior of a single job.

It also provides waterfall and boxplot visualizations for jobs. We've found that
these visualizations can be really helpful for figuring out why a job is slow.
Is it launching too many mappers and overloading the cluster? Are reducers
launching early and starving the mappers? Does the job have reducer skew?
You can use the counters of bytes written, shuffled, and read to understand the
network and I/O behavior of your jobs. And when there's a crash, Timberlake will
show you tracebacks from the logs to help you debug the job.

Timberlake pairs well with Scalding and Cascading. It uses extra data from the
Cascading planner to show the relationships between steps, and to clarify which
jobs' outputs are used as inputs to other jobs in the flow. Visualizing that
flow makes it much easier to figure out which steps are causing bottlenecks.

Finally, we've included a Slackbot that has significantly improved our Hadooping
lives. The bot can notify you when your jobs start and finish, and provides
links back to Timberlake.

## Screenshots

#### Job Details
![Job Details](https://cloud.githubusercontent.com/assets/57258/5138257/b65377fe-7100-11e4-89b9-13fbacf411b1.png)

#### List of Jobs
![List of Jobs](https://cloud.githubusercontent.com/assets/57258/5137476/f755b92c-70ee-11e4-8d6f-6819e5035529.png)

## Installation

The best way to install is with tarballs, which are available on the
[release page](https://github.com/stripe/timberlake/releases).

Download it somewhere on your server, and then untar it:

    $ tar zxvf timberlake-v1.0.2-linux-amd64.tar.gz
    $ mv -T timberlake-v1.0.2-linux-amd64 /opt/timberlake

Now you can start the server:

    $ /opt/timberlake/bin/timberlake \
        --bind :8000 \
        --resource-manager-url http://resourcemanager:8088 \
        --history-server-url http://resourcemanager:19888 \
        --namenode-address namenode:9000

And optionally, start the Slackbot:

    $ /opt/timberlake/bin/slack \
        --internal-timberlake-url http://localhost:8000 \
        --external-timberlake-url https://timberlake.example.com \
        --slack-url https://hooks.slack.com/services/...

You'll need to create a new [Incoming Webhook](https://slack.com/services)
to generate the Slack URL for your bot.

## Building from Source

You'll need `npm`, `go` and `node` on your path.

    $ go get -u golang.org/x/lint/golint \
        github.com/colinmarc/hdfs \
        github.com/zenazn/goji \
        github.com/stretchr/testify

    $ git clone https://github.com/stripe/timberlake.git
    $ cd timberlake
    $ make

## Limitations

Timberlake only works with the [YARN Resource Manager
API](https://hadoop.apache.org/docs/r2.5.2/hadoop-yarn/hadoop-yarn-site/ResourceManagerRest.html).
It's been tested on v2.4.x and v2.5.x, but the Kill Job feature uses an endpoint
that's only available in v2.5.x+.

Our cluster has 10-40 jobs running simultaneously and about 2,000 jobs running
per day. Timberlake's performance has not been tested outside these bounds.


text:
template <class T, size_t N>
class RingBuffer
{
    // static_assert(std::is_trivially_copyable_v<T>,
    //         "RingBuffer value type must be TriviallyCopyable");
    static_assert(std::is_nothrow_constructible_v<T>,
            "RingBuffer value type must be NoThrowConstructible");

public:
    using value_type = T;
    using pointer = T*;
    using const_pointer = const T*;
    using reference = T&;
    using const_reference = const T&;
    using size_type = int;

    struct iterator;
    struct const_iterator;

    constexpr RingBuffer() noexcept = default;

    // TODO: this semantic is confusing because the list will be in
    //       reversed order
    constexpr RingBuffer(std::initializer_list<T> vs) noexcept
    {
        for (auto v : vs) {
            emplace_front(v);
        }
    }

    constexpr iterator begin() noexcept { return iterator(&_data[_head], this); }
    constexpr const_iterator begin() const noexcept { return const_iterator{&_data[_head], this}; }
    constexpr iterator end() noexcept { return iterator{}; }
    constexpr const_iterator end() const noexcept { return const_iterator{}; }
    constexpr size_type size() const noexcept { return _size; }
    constexpr bool empty() const noexcept { return _size == 0; }
    constexpr bool full() const noexcept { return _size == N; }
    constexpr size_type capacity() const noexcept { return N; }

    template <class... Args>
    constexpr reference emplace_front(Args&&... args) noexcept
    {
        _head = _head == 0 ? N - 1 : _head - 1;
        new (&_data[_head]) T{args...};
        _size = std::min(_size + 1, capacity());
        return _data[_head];
    }

    constexpr reference push_front(const T& x) noexcept
    {
        return emplace_front(x);
    }

    constexpr void pop_front() noexcept
    {
        assert(!empty());
        _head = (_head + 1) % N;
        --_size;
    }

    constexpr reference operator[](size_type pos) noexcept
    {
        assert(0 <= pos && pos < _size);
        pos = (_head + pos) % N;
        return _data[pos];
    }

    constexpr const_reference operator[](size_type pos) const noexcept
    {
        assert(0 <= pos && pos < _size);
        pos = (_head + pos) % N;
        return _data[pos];
    }

    friend class iterator;
    friend class const_iterator;

private:
    constexpr static int _advance(int pos) noexcept
    {
        return (pos + 1) % N;
    }

    constexpr pointer _advance_pointer(pointer ptr) noexcept
    {
        int pos = _advance(ptr - &_data[0]);
        return &_data[pos];
    }

    constexpr const_pointer _advance_pointer(const_pointer ptr) const noexcept
    {
        int pos = _advance(ptr - &_data[0]);
        return &_data[pos];
    }

    std::array<T, N> _data;
    int              _head = 0;
    int              _size = 0;
};

template <class T, size_t N>
struct RingBuffer<T, N>::iterator
{
    constexpr iterator() noexcept = default;
    constexpr iterator(T* ptr, RingBuffer<T, N>* rb) noexcept
        : _ptr(ptr), _rb(rb) {}

    constexpr iterator& operator++() noexcept
    {
        _ptr = _rb->_advance_pointer(_ptr);
        if (_ptr == &_rb->_data[_rb->_head])
            _ptr = nullptr;
        return *this;
    }

    constexpr iterator operator++(int) noexcept
    {
        iterator tmp{*this};
        ++(*this);
        return tmp;
    }

    constexpr reference operator*() const noexcept
    {
        return *_ptr;
    }

    constexpr pointer operator->() const noexcept
    {
        return _ptr;
    }

    friend constexpr bool operator==(iterator a, iterator b) noexcept
    {
        return a._ptr == b._ptr;
    }

    friend constexpr bool operator!=(iterator a, iterator b) noexcept
    {
        return a._ptr != b._ptr;
    }

    friend class RingBuffer<T, N>::const_iterator;

private:
    T*                _ptr = nullptr;
    RingBuffer<T, N>* _rb = nullptr;
};

template <class T, size_t N>
struct RingBuffer<T, N>::const_iterator
{
    constexpr const_iterator() noexcept = default;
    constexpr const_iterator(const T* ptr, const RingBuffer<T, N>* rb) noexcept
        : _ptr(ptr), _rb(rb) {}
    constexpr const_iterator(iterator it) noexcept
        : _ptr(it._ptr), _rb(it._rb) {}

    constexpr const_iterator& operator++() noexcept
    {
        _ptr = _rb->_advance_pointer(_ptr);
        if (_ptr == &_rb->_data[_rb->_head])
            _ptr = nullptr;
        return *this;
    }

    constexpr const_iterator operator++(int) noexcept
    {
        const_iterator tmp{*this};
        ++(*this);
        return tmp;
    }

    constexpr const_reference operator*() const noexcept
    {
        return *_ptr;
    }

    constexpr const_pointer operator->() const noexcept
    {
        return _ptr;
    }

    friend constexpr bool operator==(const_iterator a, const_iterator b) noexcept
    {
        return a._ptr == b._ptr;
    }

    friend constexpr bool operator!=(const_iterator a, const_iterator b) noexcept
    {
        return a._ptr != b._ptr;
    }

private:
    const T*                _ptr = nullptr;
    const RingBuffer<T, N>* _rb = nullptr;
};


text:
package firestorm

import (
	"bytes"
	"fmt"
	"sort"

	"github.com/blevesearch/bleve/document"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/store"
)

type firestormReader struct {
	f        *Firestorm
	r        store.KVReader
	s        *Snapshot
	docCount uint64
}

func newFirestormReader(f *Firestorm) (index.IndexReader, error) {
	r, err := f.store.Reader()
	if err != nil {
		return nil, fmt.Errorf("error opening store reader: %v", err)
	}
	docCount, err := f.DocCount()
	if err != nil {
		return nil, fmt.Errorf("error opening store reader: %v", err)
	}
	rv := firestormReader{
		f:        f,
		r:        r,
		s:        f.compensator.Snapshot(),
		docCount: docCount,
	}
	return &rv, nil
}

func (r *firestormReader) TermFieldReader(term []byte, field string) (index.TermFieldReader, error) {
	fieldIndex, fieldExists := r.f.fieldCache.FieldNamed(field, false)
	if fieldExists {
		return newFirestormTermFieldReader(r, uint16(fieldIndex), term)
	}
	return newFirestormTermFieldReader(r, ^uint16(0), []byte{ByteSeparator})
}

func (r *firestormReader) DocIDReader(start, end string) (index.DocIDReader, error) {
	return newFirestormDocIDReader(r, start, end)
}

func (r *firestormReader) FieldDict(field string) (index.FieldDict, error) {
	return r.FieldDictRange(field, nil, nil)
}

func (r *firestormReader) FieldDictRange(field string, startTerm []byte, endTerm []byte) (index.FieldDict, error) {
	fieldIndex, fieldExists := r.f.fieldCache.FieldNamed(field, false)
	if fieldExists {
		return newFirestormDictionaryReader(r, uint16(fieldIndex), startTerm, endTerm)
	}
	return newFirestormDictionaryReader(r, ^uint16(0), []byte{ByteSeparator}, []byte{})
}

func (r *firestormReader) FieldDictPrefix(field string, termPrefix []byte) (index.FieldDict, error) {
	return r.FieldDictRange(field, termPrefix, incrementBytes(termPrefix))
}

func (r *firestormReader) Document(id string) (*document.Document, error) {
	docID := []byte(id)
	docNum, err := r.currDocNumForId(docID)
	if err != nil {
		return nil, err
	} else if docNum == 0 {
		return nil, nil
	}
	rv := document.NewDocument(id)
	prefix := StoredPrefixDocIDNum(docID, docNum)
	err = visitPrefix(r.r, prefix, func(key, val []byte) (bool, error) {
		safeVal := make([]byte, len(val))
		copy(safeVal, val)
		row, err := NewStoredRowKV(key, safeVal)
		if err != nil {
			return false, err
		}
		if row != nil {
			fieldName := r.f.fieldCache.FieldIndexed(row.field)
			field := r.decodeFieldType(fieldName, row.arrayPositions, row.value.GetRaw())
			if field != nil {
				rv.AddField(field)
			}
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}
	return rv, nil
}

func (r *firestormReader) decodeFieldType(name string, pos []uint64, value []byte) document.Field {
	switch value[0] {
	case 't':
		return document.NewTextField(name, pos, value[1:])
	case 'n':
		return document.NewNumericFieldFromBytes(name, pos, value[1:])
	case 'd':
		return document.NewDateTimeFieldFromBytes(name, pos, value[1:])
	}
	return nil
}

func (r *firestormReader) currDocNumForId(docID []byte) (uint64, error) {
	prefix := TermFreqPrefixFieldTermDocId(0, nil, docID)
	docNums := make(DocNumberList, 0)
	err := visitPrefix(r.r, prefix, func(key, val []byte) (bool, error) {
		tfk, err := NewTermFreqRowKV(key, val)
		if err != nil {
			return false, err
		}
		docNum := tfk.DocNum()
		docNums = append(docNums, docNum)
		return true, nil
	})
	if err != nil {
		return 0, err
	}
	if len(docNums) > 0 {
		sort.Sort(docNums)
		return docNums[0], nil
	}
	return 0, nil
}

func (r *firestormReader) DocumentFieldTerms(id string) (index.FieldTerms, error) {

	docID := []byte(id)
	docNum, err := r.currDocNumForId(docID)
	if err != nil {
		return nil, err
	} else if docNum == 0 {
		return nil, nil
	}

	rv := make(index.FieldTerms, 0)
	// walk the term freqs
	err = visitPrefix(r.r, TermFreqKeyPrefix, func(key, val []byte) (bool, error) {
		tfr, err := NewTermFreqRowKV(key, val)
		if err != nil {
			return false, err
		}
		if bytes.Compare(tfr.DocID(), docID) == 0 && tfr.DocNum() == docNum && tfr.Field() != 0 {
			fieldName := r.f.fieldCache.FieldIndexed(uint16(tfr.Field()))
			terms, ok := rv[fieldName]
			if !ok {
				terms = make([]string, 0, 1)
			}
			terms = append(terms, string(tfr.Term()))
			rv[fieldName] = terms
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	return rv, nil
}

func (r *firestormReader) Fields() ([]string, error) {
	fields := make([]string, 0)

	err := visitPrefix(r.r, FieldKeyPrefix, func(key, val []byte) (bool, error) {
		fieldRow, err := NewFieldRowKV(key, val)
		if err != nil {
			return false, err
		}
		fields = append(fields, fieldRow.Name())
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	return fields, nil
}

func (r *firestormReader) GetInternal(key []byte) ([]byte, error) {
	internalRow := NewInternalRow(key, nil)
	return r.r.Get(internalRow.Key())
}

func (r *firestormReader) DocCount() uint64 {
	return r.docCount
}

func (r *firestormReader) Close() error {
	return r.r.Close()
}

func incrementBytes(in []byte) []byte {
	rv := make([]byte, len(in))
	copy(rv, in)
	for i := len(rv) - 1; i >= 0; i-- {
		rv[i] = rv[i] + 1
		if rv[i] != 0 {
			// didn't overflow, so stop
			break
		}
	}
	return rv
}