This instance will be upgraded to Heptapod 0.21.0rc2 on 2021-04-16 at 11:00 UTC+2 (a few minutes of down time)

qt_manifestmodel_test.py 25.6 KB
Newer Older
Yuya Nishihara's avatar
Yuya Nishihara committed
1 2 3 4 5 6 7
from __future__ import absolute_import

import mock
import os
import sys
import unittest

8 9 10 11
from mercurial import (
    pycompat,
)

12
from nose.plugins.skip import SkipTest
Yuya Nishihara's avatar
Yuya Nishihara committed
13

14 15
import pytest

Yuya Nishihara's avatar
Yuya Nishihara committed
16 17 18 19 20 21 22
from tortoisehg.hgqt.qtcore import (
    QModelIndex,
    Qt,
)
from tortoisehg.hgqt.qtgui import (
    QApplication,
)
23 24

from tortoisehg.util import patchctx
Yuya Nishihara's avatar
Yuya Nishihara committed
25 26 27
from tortoisehg.hgqt import (
    thgrepo,
)
28
from tortoisehg.hgqt.manifestmodel import ManifestModel
29 30

import helpers
31

32
def setup_module():
33
    # necessary for style().standardIcon()
34
    if not isinstance(QApplication.instance(), QApplication):
35 36
        raise SkipTest

37 38 39
    global _tmpdir
    _tmpdir = helpers.mktmpdir(__name__)

40 41
def alldata(model, parent=QModelIndex()):
    return [model.data(model.index(r, 0, parent))
42
            for r in pycompat.xrange(model.rowCount(parent))]
43 44


45 46 47
class ManifestModelTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
48
        cls.hg = hg = helpers.HgClient(os.path.join(_tmpdir, cls.__name__))
49 50 51 52
        hg.init()
        hg.ftouch('foo', 'bar', 'baz/bax', 'baz/box')
        hg.addremove()
        hg.commit('-m', 'foobar')
53

54 55 56 57 58
        hg.fwrite('bar', 'hello\n')
        hg.remove('baz/box')
        hg.ftouch('zzz')
        hg.addremove()
        hg.commit('-m', 'remove baz/box, add zzz, modify bar')
59 60 61 62 63 64 65 66

        hg.clone('-U', '.', 'sub')
        hg.fappend('.hgsub', 'sub = sub\n')
        hg.commit('-Am', 'add empty sub')

        hg.update('-R', 'sub', '1')
        hg.commit('-Am', 'update sub')

67
        os.makedirs(hg.wjoin(b'deep'))
68 69 70 71 72 73 74 75 76
        hg.clone('-r0', '.', 'deep/sub')
        hg.fappend('.hgsub', 'deep/sub = deep/sub\n')
        hg.commit('-m', 'add deep/sub')

        hg.clone('-r0', '.', 'sub/nested')
        hg.fappend('sub/.hgsub', 'nested = nested')
        hg.addremove('-R', 'sub')
        hg.commit('-Sm', 'add sub/nested')

77 78 79
        hg.fappend('sub/bar', 'hello2\n')
        hg.commit('-Sm', 'modify sub/bar')

80 81 82
        hg.fwrite('.hgsub', 'deep/sub = deep/sub\n')
        hg.commit('-m', 'remove sub')

83 84 85 86 87 88 89 90 91 92 93
        hg.fappend('bar', 'hello3\n')
        hg.commit('-m', 'modify bar')

        hg.update('4')
        hg.fappend('foo', 'hello3\n')
        hg.fappend('bar', 'hello4\n')
        hg.commit('-m', 'modify foo and bar (new head)')

        hg.merge('--tool=internal:local')
        hg.commit('-m', 'merge (take local bar)')

94 95 96
        hg.branch('stable')
        hg.commit('-m', 'new branch stable (empty commit)')

97
    def setUp(self):
98 99
        repo = thgrepo.repository(path=self.hg.path)
        self.repoagent = thgrepo.RepoAgent(repo)
100

101
    def new_unpopulated_model(self, rev):
102
        m = ManifestModel(self.repoagent, rev=rev)
103
        return m
104

105 106 107 108 109
    def new_model(self, rev):
        m = self.new_unpopulated_model(rev)
        m.fetchMore(QModelIndex())
        return m

110
    def test_data(self):
111
        m = self.new_model(0)
112 113 114 115 116
        self.assertEqual('bar', m.data(m.index(1, 0)))
        self.assertEqual('baz', m.data(m.index(0, 0)))
        self.assertEqual('foo', m.data(m.index(2, 0)))

    def test_data_subdir(self):
117
        m = self.new_model(0)
118 119 120 121
        self.assertEqual('bax', m.data(m.index(0, 0, m.index(0, 0))))
        self.assertEqual('box', m.data(m.index(1, 0, m.index(0, 0))))

    def test_data_inexistent(self):
122
        m = self.new_model(0)
123 124 125
        self.assertEqual(None, m.data(QModelIndex()))
        self.assertEqual(None, m.data(m.index(0, 0, m.index(1, 0))))

126 127 128 129 130 131 132 133
    def test_data_subrepo(self):
        m = self.new_model(3)
        self.assertEqual(['baz', 'sub', '.hgsub'],  alldata(m)[:3])
        self.assertEqual(['baz', 'bar', 'foo', 'zzz'],
                         alldata(m, m.indexFromPath('sub')))

    def test_data_empty_subrepo(self):
        m = self.new_model(2)
134
        self.assertEqual(['baz', 'sub', '.hgsub'],  alldata(m)[:3])
135

136 137 138 139 140 141 142 143 144 145 146 147 148
    def test_data_deep_subrepo(self):
        m = self.new_model(4)
        self.assertEqual(['sub'], alldata(m, m.indexFromPath('deep')))
        self.assertEqual(['baz', 'bar', 'foo'],
                         alldata(m, m.indexFromPath('deep/sub')))

    def test_data_nested_subrepo(self):
        m = self.new_model(5)
        self.assertEqual(['baz', 'nested', '.hgsub'],
                         alldata(m, m.indexFromPath('sub'))[:3])
        self.assertEqual(['baz', 'bar', 'foo'],
                         alldata(m, m.indexFromPath('sub/nested')))

149 150 151 152 153 154 155 156 157 158 159
    def test_flat_data(self):
        m = self.new_model(0)
        m.setFlat(True)
        self.assertEqual(['bar', 'baz/bax', 'baz/box', 'foo'], alldata(m))

    def test_flat_data_subrepo(self):
        m = self.new_model(3)
        m.setFlat(True)
        self.assertEqual(['sub', '.hgsub', '.hgsubstate', 'bar', 'baz/bax'],
                         alldata(m)[:5])

160 161 162 163 164 165
    def test_drag_flag(self):
        m = self.new_model(0)
        # only files are drag enabled
        self.assertTrue(m.flags(m.indexFromPath('foo')) & Qt.ItemIsDragEnabled)
        self.assertFalse(m.flags(m.indexFromPath('baz')) & Qt.ItemIsDragEnabled)

166
    def test_isdir(self):
167
        m = self.new_model(3)
168 169 170
        self.assertTrue(m.isDir(m.indexFromPath('')))
        self.assertTrue(m.isDir(m.indexFromPath('baz')))
        self.assertFalse(m.isDir(m.indexFromPath('foo')))
171 172 173
        self.assertTrue(m.isDir(m.indexFromPath('sub')))
        self.assertFalse(m.isDir(m.indexFromPath('sub/foo')))
        self.assertTrue(m.isDir(m.indexFromPath('sub/baz')))
174

175 176 177 178 179
    def test_flat_isdir_subrepo(self):
        m = self.new_model(3)
        m.setFlat(True)
        self.assertTrue(m.isDir(m.indexFromPath('sub')))

180 181
    def test_rawfctx(self):
        m = self.new_model(5)
182 183 184 185 186 187 188 189 190 191
        self.check_rawfctx(m, 'foo', (5, '', 'foo'))
        self.check_rawfctx(m, 'sub', (5, '', 'sub'))
        self.check_rawfctx(m, 'sub/foo', (2, 'sub', 'foo'))
        self.check_rawfctx(m, 'sub/baz/bax', (2, 'sub', 'baz/bax'))
        self.check_rawfctx(m, 'sub/invalid', (-1, '', ''))
        self.check_rawfctx(m, 'deep/sub/foo', (0, 'deep/sub', 'foo'))
        self.check_rawfctx(m, 'sub/nested', (2, 'sub', 'nested'))
        self.check_rawfctx(m, 'sub/nested/foo', (0, 'sub/nested', 'foo'))

    def check_rawfctx(self, m, path, expected):
192
        fd = m.fileData(m.indexFromPath(path))
193 194
        self.assertEqual(expected,
                         (fd.rev(), fd.repoRootPath(), fd.canonicalFilePath()))
195 196 197 198 199 200

    def test_null_filedata(self):
        m = self.new_model(0)
        fd = m.fileData(QModelIndex())
        self.assertTrue(fd.isNull())
        self.assertFalse(fd.isValid())
201

202
    def test_rowcount(self):
203
        m = self.new_model(0)
204 205 206
        self.assertEqual(3, m.rowCount())

    def test_rowcount_subdirs(self):
207
        m = self.new_model(0)
208 209 210
        self.assertEqual(2, m.rowCount(m.index(0, 0)))

    def test_rowcount_invalid(self):
211
        m = self.new_model(0)
212 213 214
        self.assertEqual(0, m.rowCount(m.index(1, 0)))

    def test_pathfromindex(self):
215
        m = self.new_model(0)
216 217 218 219 220 221
        self.assertEqual('', m.filePath(QModelIndex()))
        self.assertEqual('bar', m.filePath(m.index(1, 0)))
        self.assertEqual('baz', m.filePath(m.index(0, 0)))
        self.assertEqual('baz/bax', m.filePath(m.index(0, 0, m.index(0, 0))))

    def test_indexfrompath(self):
222
        m = self.new_model(0)
223 224 225 226 227 228
        self.assertEqual(QModelIndex(), m.indexFromPath(''))
        self.assertEqual(m.index(1, 0), m.indexFromPath('bar'))
        self.assertEqual(m.index(0, 0), m.indexFromPath('baz'))
        self.assertEqual(m.index(0, 0, m.index(0, 0)),
                         m.indexFromPath('baz/bax'))

229 230 231 232 233 234 235
    def test_flat_indexfrompath(self):
        m = self.new_model(0)
        m.setFlat(True)
        self.assertEqual(m.index(0, 0), m.indexFromPath('bar'))
        self.assertFalse(m.indexFromPath('baz').isValid())
        self.assertEqual(m.index(1, 0), m.indexFromPath('baz/bax'))

236
    def test_subrepotype(self):
237
        m = self.new_model(5)
238
        self.assertEqual('hg', m.subrepoType(m.indexFromPath('sub')))
239 240
        self.assertEqual('hg', m.subrepoType(m.indexFromPath('deep/sub')))
        self.assertEqual('hg', m.subrepoType(m.indexFromPath('sub/nested')))
241 242
        self.assertEqual(None, m.subrepoType(m.indexFromPath('foo')))

243
    def test_removed_should_be_listed(self):
244
        m = self.new_model(1)
245 246 247
        m.setStatusFilter('MARC')
        self.assertTrue(m.indexFromPath('baz/box').isValid())

248 249 250 251 252 253 254
    def test_removed_subrepo(self):
        m = self.new_model(7)
        m.setStatusFilter('MARCS')
        self.assertTrue(m.indexFromPath('sub').isValid())
        self.assertEqual('R', m.subrepoStatus(m.indexFromPath('sub')))
        self.assertEqual('hg', m.subrepoType(m.indexFromPath('sub')))

255
    def test_status_role(self):
256
        m = self.new_model(0)
257 258 259
        self.assertEqual('A', m.data(m.indexFromPath('foo'),
                                     role=ManifestModel.StatusRole))

260
        m = self.new_model(1)
261 262 263 264 265 266
        m.setStatusFilter('MARC')
        self.assertEqual('C', m.data(m.indexFromPath('foo'),
                                     role=ManifestModel.StatusRole))
        self.assertEqual('R', m.data(m.indexFromPath('baz/box'),
                                     role=ManifestModel.StatusRole))

267 268 269 270
        m = self.new_model(2)
        self.assertEqual('S', m.data(m.indexFromPath('sub'),
                                     role=ManifestModel.StatusRole))

271
    def test_status_role_invalid(self):
272
        m = self.new_model(0)
273 274 275
        self.assertEqual(None, m.data(QModelIndex(),
                                      role=ManifestModel.StatusRole))

276 277 278 279 280 281 282 283 284 285 286 287 288
    def test_status_in_subrepo(self):
        # it should show changes from the parent revision of the _main_ repo,
        # not the parent of the sub repo itself
        m = self.new_model(3)
        self.assertEqual('A', m.fileStatus(m.indexFromPath('sub/foo')))
        self.assertEqual('A', m.fileStatus(m.indexFromPath('sub/bar')))
        m = self.new_model(4)
        self.assertEqual('C', m.fileStatus(m.indexFromPath('sub/foo')))
        self.assertEqual('C', m.fileStatus(m.indexFromPath('sub/bar')))
        m = self.new_model(6)
        self.assertEqual('C', m.fileStatus(m.indexFromPath('sub/foo')))
        self.assertEqual('M', m.fileStatus(m.indexFromPath('sub/bar')))

289 290 291 292 293 294 295 296
    def test_subrepo_status(self):
        m = self.new_model(2)
        self.assertEqual('A', m.subrepoStatus(m.indexFromPath('sub')))
        m = self.new_model(3)
        self.assertEqual('M', m.subrepoStatus(m.indexFromPath('sub')))
        m = self.new_model(4)
        self.assertEqual('C', m.subrepoStatus(m.indexFromPath('sub')))

297 298 299
    def test_subrepo_rev(self):
        m = self.new_model(2)
        self.assertEqual(-1, m.rev(m.indexFromPath('sub')))
300
        self.assertEqual(-1, m.baseRev(m.indexFromPath('sub')))
301 302
        m = self.new_model(3)
        self.assertEqual(1, m.rev(m.indexFromPath('sub')))
303 304 305
        self.assertEqual(-1, m.baseRev(m.indexFromPath('sub')))
        m = self.new_model(4)
        self.assertEqual(1, m.baseRev(m.indexFromPath('sub')))
306

307
    def test_status_filter_modified(self):
308
        m = self.new_model(1)
309 310 311 312 313 314 315
        m.setStatusFilter('M')
        self.assertNotEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertEqual(QModelIndex(), m.indexFromPath('zzz'))  # added
        self.assertEqual(QModelIndex(), m.indexFromPath('baz/box'))  # removed
        self.assertEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

    def test_status_filter_added(self):
316
        m = self.new_model(1)
317 318 319 320 321 322 323
        m.setStatusFilter('A')
        self.assertEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertNotEqual(QModelIndex(), m.indexFromPath('zzz'))  # added
        self.assertEqual(QModelIndex(), m.indexFromPath('baz/box'))  # removed
        self.assertEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

    def test_status_filter_removed(self):
324
        m = self.new_model(1)
325 326 327 328 329 330 331
        m.setStatusFilter('R')
        self.assertEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertEqual(QModelIndex(), m.indexFromPath('zzz'))  # added
        self.assertNotEqual(QModelIndex(), m.indexFromPath('baz/box'))  # removed
        self.assertEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

    def test_status_filter_clean(self):
332
        m = self.new_model(1)
333 334 335 336 337 338 339
        m.setStatusFilter('C')
        self.assertEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertEqual(QModelIndex(), m.indexFromPath('zzz'))  # added
        self.assertEqual(QModelIndex(), m.indexFromPath('baz/box'))  # removed
        self.assertNotEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

    def test_status_filter_change(self):
340
        m = self.new_model(1)
341 342 343 344 345 346 347 348
        m.setStatusFilter('C')
        self.assertEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertNotEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

        m.setStatusFilter('M')
        self.assertNotEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

349 350 351 352 353 354 355 356
    def test_status_filter_empty_subrepo(self):
        m = self.new_model(2)
        m.setStatusFilter('AS')
        self.assertTrue(m.indexFromPath('sub').isValid())

    def test_status_filter_deep_subrepo(self):
        m = self.new_model(4)
        m.setStatusFilter('AS')
357
        self.assertTrue(m.indexFromPath('deep/sub').isValid())
358 359 360 361 362 363 364 365 366 367 368
        m.setStatusFilter('CS')
        self.assertFalse(m.indexFromPath('deep/sub').isValid())

    def test_status_filter_nested_subrepo(self):
        m = self.new_model(5)
        # 'sub' is M, but 'sub/bar', etc. are C
        m.setStatusFilter('CS')
        self.assertTrue(m.indexFromPath('sub').isValid())
        self.assertFalse(m.indexFromPath('sub/nested').isValid())
        # 'sub' is M, but 'sub/nested' is A
        m.setStatusFilter('AS')
369
        self.assertTrue(m.indexFromPath('sub').isValid())
370
        self.assertTrue(m.indexFromPath('sub/nested').isValid())
371

372
    def test_status_filter_multi(self):
373
        m = self.new_model(1)
374 375 376 377 378 379 380
        m.setStatusFilter('MC')
        self.assertNotEqual(QModelIndex(), m.indexFromPath('bar'))  # modified
        self.assertEqual(QModelIndex(), m.indexFromPath('zzz'))  # added
        self.assertEqual(QModelIndex(), m.indexFromPath('baz/box'))  # removed
        self.assertNotEqual(QModelIndex(), m.indexFromPath('foo'))  # clean

    def test_name_filter(self):
381
        m = self.new_model(0)
382 383 384 385 386 387 388
        m.setNameFilter('ax')
        self.assertFalse(m.indexFromPath('bar').isValid())
        self.assertTrue(m.indexFromPath('baz/bax').isValid())
        self.assertFalse(m.indexFromPath('baz/box').isValid())
        self.assertFalse(m.indexFromPath('foo').isValid())

    def test_name_filter_glob(self):
389
        m = self.new_model(0)
390 391 392 393 394 395
        m.setNameFilter('b*x')
        self.assertFalse(m.indexFromPath('bar').isValid())
        self.assertTrue(m.indexFromPath('baz/bax').isValid())
        self.assertTrue(m.indexFromPath('baz/box').isValid())
        self.assertFalse(m.indexFromPath('foo').isValid())

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
    def test_name_filter_fileset(self):
        m = self.new_model(0)
        m.setNameFilter('relpath:foo')
        self.assertFalse(m.indexFromPath('bar').isValid())
        self.assertTrue(m.indexFromPath('foo').isValid())

    def test_name_filter_invalid_path(self):
        m = self.new_model(0)
        m.setNameFilter('relpath:/foo')  # no "Abort: /foo not under root"
        self.assertEqual(0, m.rowCount())

    def test_name_filter_unparsable(self):
        m = self.new_model(0)
        m.setNameFilter('set:invalid_query(')  # no ParseError
        self.assertEqual(0, m.rowCount())

412 413 414 415 416 417 418
    def test_name_filter_subrepo(self):
        m = self.new_model(3)
        m.setNameFilter('ba*')
        self.assertFalse(m.indexFromPath('sub/foo').isValid())
        self.assertTrue(m.indexFromPath('sub/bar').isValid())
        self.assertTrue(m.indexFromPath('sub/baz/bax').isValid())

419 420 421 422 423 424 425
    def test_name_filter_empty_subrepo(self):
        m = self.new_model(2)
        m.setNameFilter('sub')
        self.assertTrue(m.indexFromPath('sub').isValid())
        m.setNameFilter('foo')
        self.assertFalse(m.indexFromPath('sub').isValid())

426 427 428 429 430 431 432 433 434 435 436 437 438
    def test_changedfiles_filter_merge(self):
        m = self.new_model(10)
        m.setStatusFilter('MARS')
        self.assertEqual(['sub', '.hgsub', '.hgsubstate', 'bar'], alldata(m))
        m.setChangedFilesOnly(True)
        self.assertEqual(['bar'], alldata(m))

    def test_changedfiles_filter_emptycommit(self):
        m = self.new_model(11)
        self.assertTrue(m.rowCount() > 0)
        m.setChangedFilesOnly(True)
        self.assertEqual(0, m.rowCount())

439 440 441 442 443 444 445 446 447 448 449 450 451 452
    def test_change_rev(self):
        m = self.new_model(-1)
        self.assertEqual(-1, m.rev())
        m.setRev(0)
        self.assertEqual(0, m.rev())
        self.assertEqual(['baz', 'bar', 'foo'], alldata(m))

    def test_change_rev_unpopulated(self):
        m = self.new_unpopulated_model(0)
        self.assertTrue(m.canFetchMore(QModelIndex()))
        m.setRev(1)
        self.assertEqual(1, m.rev())
        self.assertTrue(m.canFetchMore(QModelIndex()))

453 454 455 456 457 458 459 460
    def test_change_rev_by_ctx(self):
        repo = self.repoagent.rawRepo()
        m = self.new_model(0)
        m.setRawContext(repo[1])
        self.assertEqual(1, m.rev())
        # should not fall back to patch handler
        self.assertTrue(m.flags(m.indexFromPath('foo')) & Qt.ItemIsDragEnabled)

461 462 463 464 465 466 467 468 469 470 471
    def test_change_base_rev(self):
        m = self.new_model(10)
        m.setStatusFilter('MARS')
        self.assertEqual(9, m.baseRev())
        m.setRev(10, m.SecondParent)
        self.assertEqual(8, m.baseRev())
        self.assertEqual(['bar', 'foo'], alldata(m))
        m.setRev(10, 4)
        self.assertEqual(['sub', '.hgsub', '.hgsubstate', 'bar', 'foo'],
                         alldata(m))

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    def test_rev_loaded_on_initial_population(self):
        m = self.new_unpopulated_model(0)
        loaded = mock.Mock()
        m.revLoaded.connect(loaded)
        m.setRev(1)
        self.assertFalse(loaded.called, 'not populated but loaded emitted')
        m.fetchMore(QModelIndex())
        loaded.assert_called_once_with(1)

    def test_rev_loaded_on_change(self):
        m = self.new_model(0)
        loaded = mock.Mock()
        m.revLoaded.connect(loaded)
        m.setRev(1)
        loaded.assert_called_once_with(1)

    def test_no_rev_loaded_on_filter_change(self):
        m = self.new_model(0)
        loaded = mock.Mock()
        m.revLoaded.connect(loaded)
        m.setNameFilter('foo')
        self.assertFalse(loaded.called)

495 496 497 498 499 500 501
    def test_change_flat_unpopulated(self):
        m = self.new_unpopulated_model(0)
        self.assertTrue(m.canFetchMore(QModelIndex()))
        m.setFlat(True)
        self.assertTrue(m.isFlat())
        self.assertTrue(m.canFetchMore(QModelIndex()))

502

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
class ManifestModelPatchTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.hg = hg = helpers.HgClient(os.path.join(_tmpdir, cls.__name__))
        hg.init()
        hg.ftouch('foo', 'bar/baz')
        # patchctx can't detect status without content
        hg.fwrite('foo', 'hello1\n')
        hg.fwrite('bar/baz', 'hello2\n')
        hg.addremove()
        hg.qnew('patch0.diff')

        hg.fappend('foo', 'hello3\n')
        hg.remove('bar/baz')
        hg.qnew('patch1.diff')

        hg.qpop('-a')

    def setUp(self):
        repo = thgrepo.repository(path=self.hg.path)
        self.repoagent = thgrepo.RepoAgent(repo)

    def new_model(self, patch):
        m = ManifestModel(self.repoagent)
        repo = self.repoagent.rawRepo()
        m.setRawContext(repo[patch])
        m.fetchMore(QModelIndex())
        return m

    def test_data(self):
        m = self.new_model('patch0.diff')
        self.assertEqual(['bar', 'foo'], alldata(m))

    def test_no_drag_flag(self):
        m = self.new_model('patch0.diff')
        self.assertFalse(m.flags(m.indexFromPath('foo')) & Qt.ItemIsDragEnabled)

    def test_status(self):
        m = self.new_model('patch0.diff')
        self.assertEqual('A', m.fileStatus(m.indexFromPath('foo')))
        m = self.new_model('patch1.diff')
        m.setStatusFilter('MARC')
        self.assertEqual('M', m.fileStatus(m.indexFromPath('foo')))
        self.assertEqual('R', m.fileStatus(m.indexFromPath('bar/baz')))

    def test_name_filter(self):
        m = self.new_model('patch0.diff')
        m.setNameFilter('bar')
        self.assertEqual(['bar'], alldata(m))


554 555 556 557 558 559 560 561 562 563 564 565
class ManifestModelReloadTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.hg = hg = helpers.HgClient(os.path.join(_tmpdir, cls.__name__))
        hg.init()
        hg.ftouch('foo')
        hg.addremove()
        hg.qnew('patch0.diff')

        hg.qinit('-c')
        hg.commit('--mq', '-m', 'initial state')

566 567 568
        cls.mtimedelay = helpers.guessmtimedelay(hg.path)
        hg.fappend('.hg/hgrc', '[debug]\ndelaylock = %d\n' % cls.mtimedelay)

569 570 571 572 573 574
    def setUp(self):
        repo = thgrepo.repository(path=self.hg.path)
        repo.thginvalidate()  # because of global cache of thgrepo instance
        self.repoagent = thgrepo.RepoAgent(repo)

    def tearDown(self):
575 576 577 578
        self.hg.revert('-a', '--config', 'debug.delaylock=0')
        self.hg.qpop('-a', '--config', 'debug.delaylock=0')
        self.hg.update('--mq', '-C', '--config', 'debug.delaylock=0')
        self.hg.qpush('-a', '--config', 'debug.delaylock=0')
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605

    def new_model(self, rev=0):
        m = ManifestModel(self.repoagent, rev=rev)
        m.fetchMore(QModelIndex())
        return m

    def add_file(self, name):
        self.hg.ftouch(name)
        self.hg.addremove()
        self.hg.qrefresh()
        self.repoagent.pollStatus()

    def test_samerev_but_node_differs(self):
        m = self.new_model()
        self.assertEqual(['foo'], alldata(m))
        self.add_file('bar')
        m.setRev(m.rev())
        self.assertEqual(['bar', 'foo'], alldata(m))

    def test_samerevctx_but_node_differs(self):
        m = self.new_model()
        self.assertEqual(['foo'], alldata(m))
        self.add_file('bar')
        repo = self.repoagent.rawRepo()
        m.setRawContext(repo[m.rev()])
        self.assertEqual(['bar', 'foo'], alldata(m))

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    def add_workingfile(self, name):
        self.hg.ftouch(name)
        self.hg.addremove()
        repo = self.repoagent.rawRepo()
        repo.thginvalidate()

    def test_workingrev_changed(self):
        m = self.new_model(None)
        self.assertEqual(['foo'], alldata(m))
        self.add_workingfile('bar')
        m.setRev(m.rev())
        self.assertEqual(['bar', 'foo'], alldata(m))

    def test_workingctx_changed(self):
        m = self.new_model(None)
        self.assertEqual(['foo'], alldata(m))
        self.add_workingfile('bar')
        repo = self.repoagent.rawRepo()
        m.setRawContext(repo[None])
        self.assertEqual(['bar', 'foo'], alldata(m))

    def test_patchctx_changed(self):
        repo = self.repoagent.rawRepo()
        ctx1 = patchctx.patchctx(repo.mq.join('patch0.diff'), repo)
        ctx1.files()  # load
        self.add_file('bar')
        ctx2 = patchctx.patchctx(repo.mq.join('patch0.diff'), repo)
        ctx2.files()  # load

        m = self.new_model()
        m.setRawContext(ctx1)
        self.assertEqual(['foo'], alldata(m))
        m.setRawContext(ctx2)
        self.assertEqual(['bar', 'foo'], alldata(m))

641

642
@pytest.mark.largefiles
643 644 645
class ManifestModelLargeFilesTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
646
        cls.hg = hg = helpers.HgClient(os.path.join(_tmpdir, cls.__name__))
647 648 649 650 651 652 653 654 655 656 657
        hg.init()
        hg.fwrite('.hg/hgrc', '[extensions]\nlargefiles=\n')

        hg.ftouch('foo', 'bar', 'large', 'baz/large')
        hg.add('foo', 'bar')
        hg.add('--large', 'large', 'baz/large')
        hg.commit('-m', 'add regular and large files')

        hg.fappend('large', 'hello largefiles\n')
        hg.commit('-m', 'edit large')

658
    def setUp(self):
659 660
        repo = thgrepo.repository(path=self.hg.path)
        self.repoagent = thgrepo.RepoAgent(repo)
661 662

    def new_model(self, rev):
663 664 665
        m = ManifestModel(self.repoagent, rev=rev)
        m.fetchMore(QModelIndex())
        return m
666 667 668 669 670 671 672 673 674 675 676 677

    def test_data(self):
        m = self.new_model(0)
        self.assertEqual(['baz', 'bar', 'foo', 'large'], alldata(m))
        self.assertEqual(['large'], alldata(m, m.indexFromPath('baz')))

    def test_status(self):
        m = self.new_model(1)
        self.assertEqual('M', m.fileStatus(m.indexFromPath('large')))
        self.assertEqual('C', m.fileStatus(m.indexFromPath('baz/large')))


678
_aloha_ja = u'\u3042\u308d\u306f\u30fc'
679

680 681 682
class ManifestModelEucjpTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
683 684
        # TODO: make this compatible with binary-unsafe filesystem
        if os.name != 'posix' or sys.platform == 'darwin':
685 686 687
            raise SkipTest
        cls.encodingpatch = helpers.patchencoding('euc-jp')

688
        # include non-ascii char in repo path to test concatenation
689
        cls.hg = hg = helpers.HgClient(os.path.join(
690
            _tmpdir, cls.__name__ + _aloha_ja.encode('euc-jp')))
691 692
        hg.init()
        hg.ftouch(_aloha_ja.encode('euc-jp'))
693
        hg.ftouch(_aloha_ja.encode('euc-jp') + '.txt')
694 695 696 697 698 699 700
        hg.addremove()
        hg.commit('-m', 'add aloha')

    @classmethod
    def tearDownClass(cls):
        cls.encodingpatch.restore()

701
    def setUp(self):
702 703
        repo = thgrepo.repository(path=self.hg.path)
        self.repoagent = thgrepo.RepoAgent(repo)
704

705
    def new_model(self):
706 707 708
        m = ManifestModel(self.repoagent, rev=0)
        m.fetchMore(QModelIndex())
        return m
709

710
    def test_data(self):
711
        m = self.new_model()
712 713 714
        self.assertEqual(_aloha_ja, m.data(m.index(0, 0)))

    def test_pathfromindex(self):
715
        m = self.new_model()
716 717 718
        self.assertEqual(_aloha_ja, m.filePath(m.index(0, 0)))

    def test_indexfrompath(self):
719
        m = self.new_model()
720
        self.assertEqual(m.index(0, 0), m.indexFromPath(_aloha_ja))
721 722

    def test_fileicon_path_concat(self):
723
        m = self.new_model()
724
        m.fileIcon(m.indexFromPath(_aloha_ja + '.txt'))  # no unicode error